Skip to content

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

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

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

Conversation

@ALDRIN121

Copy link
Copy Markdown
Contributor

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
@ALDRIN121
ALDRIN121 force-pushed the fix-events-resilient-state-serialization branch from d73ec7e to e04d820 Compare September 1, 2026 16:51
copybara-service Bot pushed a commit that referenced this pull request Sep 5, 2026
@adk-bot

adk-bot commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Thank you @ALDRIN121 for your contribution! 🎉

Your changes have been successfully imported and merged via Copybara in commit a119dd7.

Closing this PR as the changes are now in the main branch.

@adk-bot adk-bot added the merged [Status] This PR is merged label Sep 5, 2026
@adk-bot adk-bot closed this Sep 5, 2026
copybara-service Bot pushed a commit that referenced this pull request Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merged [Status] This PR is merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants