langchain-ai/deepagents · error · TypeError
hook_responses must be a JSON object.
Error message
hook_responses must be a JSON object.
What it means
`hook_responses` holds replies to any pending hook questions and must be a JSON object (dict). The field defaults to {} when absent, but if explicitly provided it must be a dict; lists, strings, or None are rejected. Keys are later normalized to strings ({str(key): value}).
Source
Thrown at libs/code/deepagents_code/offload_api.py:528
Raises:
TypeError: If the payload or a structured field has the wrong shape.
"""
if not isinstance(payload, dict):
msg = "Offload request must be a JSON object."
raise TypeError(msg)
operation_id = payload.get("operation_id")
context = payload.get("context")
responses = payload.get("hook_responses", {})
if not isinstance(operation_id, str) or not operation_id:
msg = "operation_id must be a non-empty string."
raise TypeError(msg)
if not isinstance(context, dict):
msg = "context must be a JSON object."
raise TypeError(msg)
if not isinstance(responses, dict):
msg = "hook_responses must be a JSON object."
raise TypeError(msg)
validated_context = {str(key): value for key, value in context.items()}
_validate_context(validated_context)
return (
operation_id,
_strip_transport_model_params(validated_context),
{str(key): value for key, value in responses.items()},
)
def _hydrate_state(values: object) -> _OffloadState:
"""Hydrate serialized checkpoint messages for the compaction service.
Args:
values: State values returned by LangGraph Server.
Returns:
A shallow state copy containing LangChain message objects.
View on GitHub (pinned to a1af029e6e)
Solutions
- Key responses by hook id: {'<hook-id>': <answer>} as a dict, not a list.
- Omit the key entirely or pass {} when there are no hook responses — never None.
- If responses arrive as JSON text, json.loads() them and confirm the result is a dict.
- If you have a list of {id, answer} records, convert: {r['id']: r['answer'] for r in records}.
Example fix
// before
payload = {"operation_id": "op1", "context": {}, "hook_responses": None}
// after
payload = {"operation_id": "op1", "context": {}, "hook_responses": {}} Defensive patterns
Strategy: validation
Validate before calling
def ensure_hook_responses(payload):
responses = payload.get("hook_responses", {})
if responses is None:
responses = {}
if not isinstance(responses, dict):
raise ValueError("hook_responses must be a dict keyed by hook id")
payload["hook_responses"] = {str(k): v for k, v in responses.items()}
return payload Type guard
def has_valid_hook_responses(payload) -> bool:
responses = payload.get("hook_responses", {})
return responses is None or isinstance(responses, dict) Try / catch
try:
state = await offload(thread_id, payload)
except TypeError as exc:
if "hook_responses" in str(exc):
payload["hook_responses"] = {}
state = await offload(thread_id, payload)
else:
raise Prevention
- Model hook responses as a mapping {hook_id: answer}, never a list
- Omit the key or use {} when there are no responses — never None
- Normalize keys with str(k) if ids may be non-strings
- Decode response JSON text before embedding it
When it happens
Trigger: Calling offload() with payload={'hook_responses': None}, {'hook_responses': [{'hook_id': ..., 'answer': ...}]}, or {'hook_responses': 'response-json'}.
Common situations: Passing a list of response records instead of a mapping keyed by hook/question id, serializing the responses dict to a string before embedding, passing None for 'no responses' instead of omitting the key or using {}.
Related errors
- context.hooks_server_events must be a list of strings or nul
- Offload request must be a JSON object.
- operation_id must be a non-empty string.
- context must be a JSON object.
- interpreter_ptc must be False, 'safe', 'all', or a list of t
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/91faa3d46fdcf89c.
Report an issue: GitHub.