You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ToolConfirmation.from_response_dict crashes with TypeError on parsed dicts聽#7681
Describe the Bug: ToolConfirmation.from_response_dict parses tool confirmations from a response dict and intends to support both raw dicts and the wrapper format {"response": ...}. However, it assumes response["response"] is always a serialized JSON string and unconditionally calls json.loads(response["response"]). If the payload is already deserialized (e.g., by FastAPI, an HTTP client, or an in-memory runner) into {"response": {"confirmed": True}}, calling json.loads(dict) raises TypeError: the JSON object must be str, bytes or bytearray, not dict.
In Human-In-The-Loop workflows (src/google/adk/workflow/utils/_workflow_hitl_utils.py), the resume handler wraps from_response_dict in try: ... except ValueError as e:. Because TypeError does not inherit from ValueError, resumes with pre-parsed dictionary payloads crash unexpectedly.
Observe TypeError: the JSON object must be str, bytes or bytearray, not dict
Expected Behavior: ToolConfirmation.from_response_dict should accept pre-parsed dictionary payloads inside response["response"] and validate them directly via model_validate(raw).
Observed Behavior:
TypeError: the JSON object must be str, bytes or bytearray, not dict
Environment Details:
Desktop OS: Windows / Linux / macOS
Python Version: 3.11+
ADK Library Version: main branch
Model Information:
Are you using LiteLLM: No
Which model is being used: N/A
馃煛 Optional Information
Minimal Reproduction Code:
python
from google.adk.tools.tool_confirmation import ToolConfirmation
Crashes with TypeError:
ToolConfirmation.from_response_dict({"response": {"confirmed": True}})
Reproduced this on main @aryanpatil-07 and the TypeError also leaks out of parse_tool_confirmation() in the workflow HITL resume path instead of becoming a WorkflowDataError.
The approach in #7682 looks right as a workaround: only call json.loads() when the inner value is a str/bytes, otherwise pass it straight to model_validate(). Invalid values like {"response": 123} then raise a ValidationError which the HITL code already catches.
Could you confirm this covers your real case too? Also which caller sent you the pre-parsed dict (FastAPI, a custom client, workflow resume)?
Before it goes for review please test it well and add a test for the HITL resume path and for a non-dict inner value so we cover more than the direct call.
The pre-parsed dict came from a FastAPI / REST API server setup. The incoming HTTP request body containing the resume payload was parsed by FastAPI into Python dictionaries before being passed to runner.run_async(..., resume_inputs=...) / workflow resume. Because the client payload used the wrapper shape {"response": {"confirmed": True, ...}}, response["response"] was already a dict rather than a JSON string, which triggered the TypeError in json.loads().
As requested, I have updated the PR with:
Non-dict inner value test: Added test_single_response_key_with_invalid_inner_value_raises_validation_error in test_tool_confirmation.py, verifying that {"response": 123} raises ValidationError.
Activity
surajksharma07 commentedon Oct 23, 2026
Reproduced this on main @aryanpatil-07 and the TypeError also leaks out of parse_tool_confirmation() in the workflow HITL resume path instead of becoming a WorkflowDataError.
The approach in #7682 looks right as a workaround: only call json.loads() when the inner value is a str/bytes, otherwise pass it straight to model_validate(). Invalid values like {"response": 123} then raise a ValidationError which the HITL code already catches.
Could you confirm this covers your real case too? Also which caller sent you the pre-parsed dict (FastAPI, a custom client, workflow resume)?
Before it goes for review please test it well and add a test for the HITL resume path and for a non-dict inner value so we cover more than the direct call.
aryanpatil-07 commentedon Oct 23, 2026
Hi @surajksharma07 , thanks for reproducing and reviewing!
Yes, this covers our real case completely.
The pre-parsed dict came from a FastAPI / REST API server setup. The incoming HTTP request body containing the resume payload was parsed by FastAPI into Python dictionaries before being passed to
runner.run_async(..., resume_inputs=...)/ workflow resume. Because the client payload used the wrapper shape{"response": {"confirmed": True, ...}},response["response"]was already adictrather than a JSON string, which triggered theTypeErrorinjson.loads().As requested, I have updated the PR with:
test_single_response_key_with_invalid_inner_value_raises_validation_errorintest_tool_confirmation.py, verifying that{"response": 123}raisesValidationError.test_workflow_hitl_utils.pyverifying that:{"response": {"confirmed": True}}) successfully setsctx.tool_confirmation.{"response": 123}) properly get converted toWorkflowDataErrorinstead of leakingTypeError.All checks are passing and it is ready for another review!