{"record":{"id":"91faa3d46fdcf89c","repo":"langchain-ai/deepagents","slug":"hook-responses-must-be-a-json-object","errorCode":null,"errorMessage":"hook_responses must be a JSON object.","messagePattern":"hook_responses must be a JSON object\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/offload_api.py","lineNumber":528,"sourceCode":"\n    Raises:\n        TypeError: If the payload or a structured field has the wrong shape.\n    \"\"\"\n    if not isinstance(payload, dict):\n        msg = \"Offload request must be a JSON object.\"\n        raise TypeError(msg)\n    operation_id = payload.get(\"operation_id\")\n    context = payload.get(\"context\")\n    responses = payload.get(\"hook_responses\", {})\n    if not isinstance(operation_id, str) or not operation_id:\n        msg = \"operation_id must be a non-empty string.\"\n        raise TypeError(msg)\n    if not isinstance(context, dict):\n        msg = \"context must be a JSON object.\"\n        raise TypeError(msg)\n    if not isinstance(responses, dict):\n        msg = \"hook_responses must be a JSON object.\"\n        raise TypeError(msg)\n    validated_context = {str(key): value for key, value in context.items()}\n    _validate_context(validated_context)\n    return (\n        operation_id,\n        _strip_transport_model_params(validated_context),\n        {str(key): value for key, value in responses.items()},\n    )\n\n\ndef _hydrate_state(values: object) -> _OffloadState:\n    \"\"\"Hydrate serialized checkpoint messages for the compaction service.\n\n    Args:\n        values: State values returned by LangGraph Server.\n\n    Returns:\n        A shallow state copy containing LangChain message objects.\n","sourceCodeStart":510,"sourceCodeEnd":546,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/offload_api.py#L510-L546","documentation":"`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}).","triggerScenarios":"Calling offload() with payload={'hook_responses': None}, {'hook_responses': [{'hook_id': ..., 'answer': ...}]}, or {'hook_responses': 'response-json'}.","commonSituations":"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 {}.","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}."],"exampleFix":"// before\npayload = {\"operation_id\": \"op1\", \"context\": {}, \"hook_responses\": None}\n// after\npayload = {\"operation_id\": \"op1\", \"context\": {}, \"hook_responses\": {}}","handlingStrategy":"validation","validationCode":"def ensure_hook_responses(payload):\n    responses = payload.get(\"hook_responses\", {})\n    if responses is None:\n        responses = {}\n    if not isinstance(responses, dict):\n        raise ValueError(\"hook_responses must be a dict keyed by hook id\")\n    payload[\"hook_responses\"] = {str(k): v for k, v in responses.items()}\n    return payload","typeGuard":"def has_valid_hook_responses(payload) -> bool:\n    responses = payload.get(\"hook_responses\", {})\n    return responses is None or isinstance(responses, dict)","tryCatchPattern":"try:\n    state = await offload(thread_id, payload)\nexcept TypeError as exc:\n    if \"hook_responses\" in str(exc):\n        payload[\"hook_responses\"] = {}\n        state = await offload(thread_id, payload)\n    else:\n        raise","preventionTips":["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"],"tags":["type-error","validation","hooks","payload"],"backgroundTag":"invalid-json-payload","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}