-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Description
_safe_json_serialize in src/google/adk/telemetry/tracing.py silently replaces Pydantic BaseModel instances with the string '<not serializable>' when serializing tool responses for tracing spans.
This affects any tool that returns a Pydantic model rather than a plain dict — the tool executes correctly, but the traced output is lost.
Root Cause
The serializer uses:
json.dumps(obj, ensure_ascii=False, default=lambda o: '<not serializable>')When a tool returns a Pydantic model, ADK wraps it as {'result': <BaseModel>} in __build_response_event (functions.py line 798-799). json.dumps can't serialize BaseModel natively, so the default lambda fires and replaces it.
Reproduction
import json
import asyncio
from pydantic import BaseModel
from google.adk.telemetry.tracing import _safe_json_serialize
class SearchResults(BaseModel):
query: str
total: int
items: list[str]
async def search_tool(query: str) -> SearchResults:
"""A tool that returns a Pydantic model."""
return SearchResults(query=query, total=2, items=["doc1", "doc2"])
result = asyncio.run(search_tool(query="test"))
# ADK wraps non-dict results (functions.py __build_response_event)
tool_response = {"result": result}
# ADK traces the response (tracing.py trace_tool_call)
serialized = _safe_json_serialize(tool_response)
print(serialized)
# Output: {"result": "<not serializable>"}
# Expected: {"result": {"query": "test", "total": 2, "items": ["doc1", "doc2"]}}Suggested Fix
BaseModel is already imported in tracing.py (line 60) and pydantic is already a dependency. The fix is minimal:
def _safe_json_serialize(obj) -> str:
def _default(o):
if isinstance(o, BaseModel):
return o.model_dump(mode="json")
return '<not serializable>'
try:
return json.dumps(obj, ensure_ascii=False, default=_default)
except (TypeError, OverflowError):
return '<not serializable>'This is consistent with how the rest of the file already handles Pydantic models (e.g. _serialize_content at line 470 calls content.model_dump()).
Impact
Every tool returning a Pydantic model has its trace output silently dropped. This affects observability integrations (Langfuse, Phoenix, etc.) that consume these spans. The tool itself works fine — only the trace is broken.
Related Issues
- Object of type AnyUrl is not JSON serializable while reading github file using MCP Tool #1540 —
AnyUrlnot serializable (MCP tools, same root cause) - {"error": "Object of type date is not JSON serializable"} #2457 —
datenot serializable (BigQuery tools, same root cause) - TypeError when using LongRunningFunctionTool: SerializationIterator is not JSON serializable during event DB insert #586, g_ui_adk.event_translator:Error translating ADK event: Object of type SecuritySchemeType is not JSON serializable #3288 — other type-specific serialization failures
All of these are symptoms of the same gap: _safe_json_serialize only handles JSON-native types.
Environment
- ADK version: 1.25.1 (also confirmed on main branch)
- Python: 3.12
- Pydantic: 2.x