fix(sessions): truncate error_message in v0 StorageEvent to guard against VARCHAR(255) overflow#5020
Conversation
JiwaniZakir
left a comment
There was a problem hiding this comment.
The test test_from_event_truncates_error_message_exceeding_varchar255 asserts len(...) <= _LEGACY_ERROR_MSG_MAX_LEN, but given the deterministic truncation logic in from_event, this should be a strict equality check (== _LEGACY_ERROR_MSG_MAX_LEN) — the weaker assertion would pass even if the truncation were accidentally off by several characters. There's also no test case for error_message=None, which exercises the falsy branch of the condition in from_event; while the logic looks correct, an explicit test would document the intent and prevent regressions. Additionally, the test file imports the private symbols _LEGACY_ERROR_MSG_MAX_LEN and _TRUNCATION_SUFFIX directly — if these ever need to be refactored or inlined, the test becomes a coupling point; consider asserting the concrete expected string ("x" * 241 + "...[truncated]") or at least the exact final length (255) to reduce that dependency.
|
Thanks for the thorough review @JiwaniZakir — all three points are valid, addressed in 9353950:
|
…inst VARCHAR(255) overflow Databases created with earlier ADK versions retain error_message as VARCHAR(255) because create_all() is additive-only and never ALTERs existing columns. When a model returns a MALFORMED_FUNCTION_CALL error with a large JSON payload, the unbounded error_message silently exceeds that limit and raises StringDataRightTruncationError, terminating the SSE stream with no fallback. The fix adds a write-path guard in StorageEvent.from_event(): if the message exceeds 255 characters it is clipped to 241 chars and suffixed with "...[truncated]", keeping the total within the legacy column width. Messages within the limit are passed through unchanged. The v1 schema is unaffected (error_message is stored inside the event_data JSONB column). The permanent fix remains running: ALTER TABLE events ALTER COLUMN error_message TYPE TEXT; or migrating to the v1 schema via `adk migrate session`. Fixes google#4993
…view
- Replace <= with == in the truncation test: the guard is deterministic
so the result should be exactly 255 chars, not merely at most 255
- Assert the concrete expected string ("x" * 241 + "...[truncated]")
rather than importing private module symbols, reducing coupling to
internal implementation details
- Add test_from_event_with_none_error_message to cover the falsy branch
of the truncation condition and document that None passes through
unchanged
9353950 to
a6e3d89
Compare
Fixes #4993.
Problem
DatabaseSessionServicecrashes on PostgreSQL databases that were created with an older ADK version whereerror_messagewas aVARCHAR(255)column.create_all()is additive-only — it neverALTERs existing columns — so those databases keep the narrow column indefinitely even after upgrading the package.When a model returns a
MALFORMED_FUNCTION_CALLerror whose payload echoes back a large JSON body, the unboundederror_messagestring exceeds 255 characters and hits:This terminates the SSE stream entirely with no fallback and no warning to the caller.
Fix
Add a write-path guard in
StorageEvent.from_event()(v0 schema only). Iferror_messageexceeds the legacy column width it is clipped to 241 characters and suffixed with"...[truncated]", keeping the total at exactly 255. Messages within the limit are passed through unchanged.The guard lives in the schema layer rather than the service layer so it is applied regardless of the call site, and it is scoped to v0 only because v1 stores everything in a
JSONB event_datacolumn and is not affected.Two constants are introduced at module level with a comment explaining the invariant, so the magic number is traceable without digging into the issue history.
Permanent fix
Users on PostgreSQL with a legacy schema can resolve this at the DB level with:
Or migrate to v1 fully via
adk migrate session.Testing
Added two unit tests to
test_v0_storage_event.py:test_from_event_truncates_error_message_exceeding_varchar255— a 300-char message is clamped to ≤ 255 chars and ends with the truncation suffixtest_from_event_preserves_short_error_message— a short message is stored verbatim