Skip to content

GoogleSearchAgentTool parallel responses fail to serialize deferred GroundingMetadata (MockValSer) #6848

Description

@andrewrfitz

🔴 Required Information

Describe the Bug:

AgentTool(propagate_grounding_metadata=True)—which GoogleSearchAgentTool
enables—stores a raw google.genai.types.GroundingMetadata instance in session
state under temp:_adk_grounding_metadata. That value reaches
EventActions.state_delta, which is typed as dict[str, Any], so event
serialization relies on Pydantic's duck-typed serialization of the value.

google-genai 2.18.0 and newer configure their generated models with
defer_build=True. A GroundingMetadata produced by nested validation (for
example, as part of Candidate validation, as happens for model responses)
leaves GroundingMetadata.__pydantic_serializer__ as a MockValSer.
Serializing the event then raises:

pydantic_core._pydantic_core.PydanticSerializationError: Error calling function `_serialize_state_delta`: TypeError: 'MockValSer' object is not an instance of 'SchemaSerializer'

The _make_json_serializable fallback introduced by #4748 does not recover
from this case. pydantic_core.to_jsonable_python(value, serialize_unknown=True) raises the same TypeError before
serialize_unknown can apply, so the exception escapes
_serialize_state_delta.

This surfaces in merge_parallel_function_response_events in
google/adk/flows/llm_flows/functions.py, which calls
event.actions.model_dump(exclude_none=True, by_alias=True). When a search call
runs in parallel with another function call, the merge raises and aborts the
invocation.

This is related to #4748 and prior grounding-metadata persistence reports such
as #5840, but it is a distinct failure mode: the value is a valid Pydantic
model whose deferred serializer has not been built.

Steps to Reproduce:

  1. Install the affected versions:

    python -m pip install "google-adk==2.7.1""google-genai==2.19.0""pydantic==2.13.4"
  2. Run the snippet under Minimal Reproduction Code below. It requires no
    model call or API key.

  3. Observe the PydanticSerializationError above.

Expected Behavior:

Serializing an event whose state_delta contains grounding metadata propagated
by ADK succeeds, and parallel function-response merging preserves both tool
results. ADK-created state should not depend on whether a deferred class
serializer happened to be built earlier in the process.

Observed Behavior:

EventActions.model_dump(...) raises from
merge_parallel_function_response_events, and the invocation terminates
without a final response.

Environment Details:

  • ADK Library Version: 2.7.1 (also reproduced on 2.7.0)
  • Desktop OS: macOS 26.6 (also reproduced on Linux)
  • Python Version: 3.14.6 (also reproduced on 3.12.6)
  • google-genai: 2.19.0 (also reproduced on 2.18.1)
  • pydantic: 2.13.4 / pydantic-core: 2.46.4

Model Information:

  • Are you using LiteLLM: No
  • Which model is being used: N/A—the minimal reproduction performs no model
    call and requires no API key.

🟡 Optional Information

Regression:

This is a google-genai 2.18.0 regression:

  • google-genai==2.17.0: nested GroundingMetadata has a built
    SchemaSerializer; the merge succeeds.
  • google-genai==2.18.0: the shared model config includes defer_build=True;
    nested GroundingMetadata retains a MockValSer; the merge fails.

The failure also reproduces with google-adk==2.6.0 plus
google-genai==2.18.1, so it is not specific to ADK 2.7.x. ADK is affected
because it propagates the raw deferred model through state_delta and then
serializes that state while merging parallel tool responses.

Logs:

Failed to serialize `state_delta`; some values are not JSON-serializable and
will be replaced with a string representation in the persisted event.
Traceback (most recent call last):
File "google/adk/events/event_actions.py", line 106, in _serialize_state_delta
return cast(dict[str, Any], handler(value))
TypeError: 'MockValSer' object is not an instance of 'SchemaSerializer'
During fallback handling, `to_jsonable_python(...)` raises the same TypeError.
The resulting PydanticSerializationError escapes the parallel-response merge.

Additional Context:

Two details make this easy to miss in tests:

  • Constructing types.GroundingMetadata(...) directly builds the serializer as
    a side effect. Nested validation via types.Candidate.model_validate(...),
    which mirrors the response-validation path, leaves the MockValSer.
  • Once anything else in the process builds this class serializer, the failure
    disappears for subsequent calls.

Possible fixes, in rough order of narrowness:

  1. Build types.GroundingMetadata before ADK propagates or serializes it, for
    example with types.GroundingMetadata.model_rebuild(force=True). Removing
    defer_build for this model in google-genai would also resolve the root
    behavior.
  2. Store grounding metadata in state_delta in serialized form, then
    revalidate it where base_llm_flow attaches it to the response.
  3. Make _make_json_serializable resilient per value so the fallback cannot
    itself abort event serialization. This is defense in depth and would
    degrade the metadata rather than preserve it.

Happy to send a PR for the preferred direction.

Minimal Reproduction Code:

fromgoogle.adk.eventsimportEvent, EventActionsfromgoogle.adk.flows.llm_flows.functionsimport (
merge_parallel_function_response_events,
)
fromgoogle.genaiimporttypescandidate=types.Candidate.model_validate(
{"groundingMetadata": {"webSearchQueries": ["some query"]}}
)
print(type(types.GroundingMetadata.__pydantic_serializer__).__name__)
# MockValSerdefresponse_event(name: str, state_delta: dict) ->Event:
returnEvent(
invocation_id="inv",
author="my_agent",
content=types.Content(
role="user",
parts=[
types.Part.from_function_response(
name=name,
response={"ok": True},
)
],
),
actions=EventActions(state_delta=state_delta),
)
events= [
response_event(
"search_agent",
{"temp:_adk_grounding_metadata": candidate.grounding_metadata},
),
response_event("other_tool", {"temp:other": 1}),
]
merge_parallel_function_response_events(events)
# PydanticSerializationError

The fallback also fails directly:

frompydantic_coreimportto_jsonable_pythonto_jsonable_python(
{"k": candidate.grounding_metadata},
serialize_unknown=True,
)
# TypeError: 'MockValSer' object is not an instance of 'SchemaSerializer'

How often has this issue occurred?:

  • Always (100%) when a deferred GroundingMetadata is present in one of two
    or more function-response events being merged.

Metadata

Metadata

Assignees

Labels

tools[Component] This issue is related to tools

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions