Skip to content

fix(events): make state serialization fallback resilient per value - #6932

Open
ALDRIN121 wants to merge 1 commit into
google:mainfrom
ALDRIN121:fix-events-resilient-state-serialization
Open

fix(events): make state serialization fallback resilient per value#6932
ALDRIN121 wants to merge 1 commit into
google:mainfrom
ALDRIN121:fix-events-resilient-state-serialization

Conversation

@ALDRIN121

Copy link
Copy Markdown

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

1. Link to an existing issue (if applicable):

This is the complementary defense-in-depth fix discussed in the #6848 thread
(agreed with @andrewrfitz and acknowledged by @llalitkumarrr): #6849 fixes the
root cause by force-building deferred Pydantic serializers; this PR makes the
serialization fallback itself resilient so a deferred model can never kill the
invocation, even if it reaches serialization.

Problem:

When google-genai >= 2.18 leaves a MockValSer placeholder on a
GroundingMetadata produced via nested validation (defer_build=True),
serializing that value with to_jsonable_python(value, serialize_unknown=True)
raises TypeError before serialize_unknown can apply. The whole
state_delta / agent_state fallback then crashes, aborting
merge_parallel_function_response_events when a search tool runs in parallel
with another tool.

Solution:

Rewrite _make_json_serializable to walk dicts/lists recursively and convert
each leaf individually under try/except, replacing only the raising values
with their repr and logging a warning that names the affected paths. Healthy
input produces output byte-identical to to_jsonable_python (enforced by a
parity test), so the existing serializers that honor callers'
exclude/include directives are unaffected. Container subclasses with broken
iteration also fall back to repr instead of escaping.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.
Run Result
pytest tests/unittests/events/test_event_actions.py 25 passed
pytest ./tests/unittests 13,315 passed, 91 skipped, 32 xfailed
tox (py3.10–3.14) py310/311/312/314: full suite passed. py313: full suite passed (13,284) on uv-managed CPython 3.13.2; this machine's system Python 3.13.1 (framework build) deterministically fails 26 unrelated tests during full-suite runs (subprocess segfaults + the pre-existing test_import_loading baseline noted in #6849) — identical with and without this change; all 26 pass standalone.
pre-commit run --files (both changed files) all 12 hooks passed

New tests reproduce the exact failure shape from the issue — a Pydantic model
whose __pydantic_serializer__ is a placeholder (_DeferredModel with
__pydantic_serializer__ = object()) — at the root, in dicts, nested in
containers, and inside state_delta / agent_state via model_dump(mode='json').

Manual End-to-End (E2E) Tests:

The issue reproduces without a model call or API key, and the deferred-model
unit tests exercise the same serialization path (EventActions.model_dump
→ wrap serializer → fallback) that crashes in
merge_parallel_function_response_events. No E2E setup beyond the unit suite
is needed for this change; the root-cause behavior is additionally covered by
#6849.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

  • Branch rebased onto current main; change is one commit, two files.
  • No public API changes; no user-facing docs impact (no adk-docs PR needed).

The _make_json_serializable fallback delegated wholesale to
pydantic_core.to_jsonable_python, which raises on deferred Pydantic models
(e.g. GroundingMetadata with MockValSer under google-genai 2.18+) before
serialize_unknown can apply, aborting event serialization and the enclosing
invocation. Walk the structure and convert each leaf under try/except,
falling back to repr with a warning naming the affected paths, so the
fallback can never raise.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants