Repository navigation
DatabaseSessionService on MySQL/MariaDB treats user ids that differ only in case or accents as the same user #7867
Description
🔴 Required Information
Describe the Bug:
Currently, DatabaseSessionService creates its tables with String(DEFAULT_MAX_KEY_LENGTH) key columns (app_name, user_id, id, session_id in schemas/v1.py) and no collation. On MySQL and MariaDB they get the database's default collation, which is case-insensitive and accent-insensitive (utf8mb4_general_ci on MariaDB, utf8mb4_0900_ai_ci on MySQL 8). Every lookup is an = comparison on those columns, so "alice", "Alice" and "ALICE" (and "rené" and "rene") are the same key. As a result:
- a new session for
"Alice"is created with theuser:state of"alice"; get_session(user_id="Alice", session_id=<alice's session id>)returns alice's session, including its events and state;list_sessions(user_id="Alice")lists alice's sessions.
InMemorySessionService, SqliteSessionService and DatabaseSessionService on SQLite or PostgreSQL keep these users apart. With the database created as COLLATE utf8mb4_bin, the same script gives the expected output. MariaDB's utf8mb4_bin is still PAD SPACE, so there "bob" and "bob " also map to one user.
Steps to Reproduce:
pip install 'google-adk[db]' aiomysqlCREATE DATABASE adk;on MariaDB or MySQL with the default collation.- Run the script below.
Expected Behavior:
None, None, 1: "Alice" is a different user from "alice", as it is with every other session service.
Observed Behavior:
alice@example.com
id='...' app_name='app' user_id='alice' state={'note': 'alice private', 'user:email': 'alice@example.com'} events=[...]
2
Environment Details:
- ADK Library Version (pip show google-adk): 2.13.0 (same
schemas/v1.pykey columns on main) - Desktop OS: macOS 26.6.2, arm64 (Darwin 25.6.0)
- Python Version (python -V): 3.12.13
- Database: MariaDB 12.3.3, aiomysql 0.3.2, SQLAlchemy 2.1.6
Model Information:
- Are you using LiteLLM: No
- Which model is being used: N/A (session service only)
🟡 Optional Information
Regression:
N/A
Additional Context:
user_id is the only isolation boundary for user: state and for get_session / list_sessions. Applications that use emails or external account names as user ids get one shared user per case or accent variant without any error. Declaring the key columns with a binary collation on MySQL/MariaDB (for example String(..., collation="utf8mb4_bin") via a dialect variant) keeps the comparison exact.
Minimal Reproduction Code:
import asyncio
from google.adk.events import Event, EventActions
from google.adk.sessions import DatabaseSessionService
DB_URL = "mysql+aiomysql://user:password@localhost:3306/adk"
async def main():
async with DatabaseSessionService(db_url=DB_URL) as svc:
alice = await svc.create_session(app_name="app", user_id="alice")
await svc.append_event(alice, Event(
author="user", invocation_id="i1",
actions=EventActions(state_delta={"user:email": "alice@example.com", "note": "alice private"}),
))
other = await svc.create_session(app_name="app", user_id="Alice")
print(other.state.get("user:email"))
# expected: None
print(await svc.get_session(app_name="app", user_id="Alice", session_id=alice.id))
# expected: None
print(len((await svc.list_sessions(app_name="app", user_id="Alice")).sessions))
# expected: 1
asyncio.run(main())How often has this issue occurred?:
- Always (100%)
Activity