Skip to content

DatabaseSessionService stores non-ASCII text as \uXXXX escapes in TEXT JSON columns #7610

Description

@marosiy

🔴 Required Information

Describe the Bug:
DatabaseSessionService stores non-ASCII text (e.g. Japanese) as \uXXXX escape sequences in JSON columns on databases that store JSON as TEXT (SQLite, MySQL, etc.). The cause is DynamicJSON.process_bind_param in src/google/adk/sessions/schemas/shared.py, which calls json.dumps(value) with the default ensure_ascii=True.

Steps to Reproduce:

  1. Create DatabaseSessionService(db_url="sqlite+aiosqlite:///sessions.db").
  2. Create a session and append an event whose text is こんにちは, or set a state value to こんにちは.
  3. Check data in DB

Expected Behavior:
The text is stored as-is (こんにちは) so it is readable when inspecting the database.

Observed Behavior:
The text is stored as \u3053\u3093\u306b\u3061\u306f in the database.

Environment Details:

  • ADK Version: 2.12.0
  • Desktop OS: macOS
  • Python Version: 3.13.7

Model Information:

  • Are you using LiteLLM: N/A
  • Which model is being used: N/A

🟡 Optional Information

Regression:
Not a regression; json.dumps has used the default.

Minimal Reproduction Code:
You can see escaped data in console.

import asyncio
import sqlite3

from google.adk.events import Event
from google.adk.sessions import DatabaseSessionService
from google.genai import types

DB_FILE = "sessions.db"


async def main():
  service = DatabaseSessionService(db_url=f"sqlite+aiosqlite:///{DB_FILE}")
  session = await service.create_session(
      app_name="app", user_id="user", state={"greeting": "こんにちは"}
  )

  await service.append_event(
      session,
      Event(
          author="user",
          content=types.Content(
              role="user", parts=[types.Part(text="こんにちは")]
          ),
      ),
  )

  # Read the raw column straight from SQLite.
  con = sqlite3.connect(DB_FILE)
  print("sessions.state   :", con.execute("select state from sessions").fetchone()[0])
  print("events.event_data:", con.execute("select event_data from events").fetchone()[0])


asyncio.run(main())

How often has this issue occurred?:

  • Always (100%)

Activity

  1. added a commit that references this issue on Oct 19, 2026
    378f23c
  2. surajksharma07 commented on Oct 19, 2026

    @surajksharma07
    Collaborator

    Reproduced this on 2.12.0 and current main with SQLite @marosiy and the root cause in DynamicJSON looks right.

    One thought on #7612: a blanket ensure_ascii=False might be risky on SQL Server where Text maps to VARCHAR(max) and non-UTF8 collations can turn raw characters into "?". A possible workaround is to keep escaping only there, e.g. return json.dumps(value, ensure_ascii=dialect.name == "mssql").

    Could you try this on your side and check that existing rows still load fine and the new rows are stored unescaped? Might also be worth a quick look at SqliteSessionService since it uses plain json.dumps too.

    Once you’ve tested it and it works feel free to update the PR and we can get it in front of the reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions