{"record":{"id":"205620b0d3706770","repo":"langchain-ai/deepagents","slug":"compiledsubagent-must-return-a-state-containing-a","errorCode":null,"errorMessage":"CompiledSubAgent must return a state containing a 'messages' key. Custom StateGraphs used with CompiledSubAgent should include 'messages' in their state schema to communicate results back to the main agent.","messagePattern":"CompiledSubAgent must return a state containing a 'messages' key\\. Custom StateGraphs used with CompiledSubAgent should include 'messages' in their state schema to communicate results back to the main agent\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/middleware/subagents.py","lineNumber":484,"sourceCode":"    subagent_description_str = \"\\n\".join(f\"- {s['name']}: {s['description']}\" for s in compiled_subagents)\n\n    # Use custom description if provided, otherwise use default template\n    if task_description is None:\n        description = TASK_TOOL_DESCRIPTION.format(available_agents=subagent_description_str)\n    elif \"{available_agents}\" in task_description:\n        description = task_description.format(available_agents=subagent_description_str)\n    else:\n        description = task_description\n\n    def _return_command_with_state_update(result: dict, tool_call_id: str) -> Command:\n        # Validate that the result contains a 'messages' key\n        if \"messages\" not in result:\n            error_msg = (\n                \"CompiledSubAgent must return a state containing a 'messages' key. \"\n                \"Custom StateGraphs used with CompiledSubAgent should include 'messages' \"\n                \"in their state schema to communicate results back to the main agent.\"\n            )\n            raise ValueError(error_msg)\n\n        state_update = {k: v for k, v in result.items() if k not in _EXCLUDED_STATE_KEYS and k not in private_state_keys}\n\n        structured = result.get(\"structured_response\")\n        if structured is not None:\n            if hasattr(structured, \"model_dump_json\"):\n                content: str = structured.model_dump_json()\n            elif dataclasses.is_dataclass(structured) and not isinstance(structured, type):\n                content = json.dumps(dataclasses.asdict(structured))\n            else:\n                content = json.dumps(structured)\n        else:\n            # Walk back to the last AIMessage with non-empty text. Anthropic\n            # occasionally emits a trailing empty `end_turn` AIMessage after a\n            # successful final tool call, which would otherwise be forwarded\n            # as an empty ToolMessage.\n            content = \"\"\n            for msg in reversed(result[\"messages\"]):","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/middleware/subagents.py#L466-L502","documentation":"When a subagent finishes, `_return_command_with_state_update` folds its result state back into the parent agent. A `CompiledSubAgent` (especially a custom `StateGraph`) must emit a state dict containing `messages`; otherwise the library cannot communicate results back and raises `ValueError`.","triggerScenarios":"Invoking a `task` tool whose subagent is a custom `CompiledSubAgent` wrapping a StateGraph whose output state (or state schema) lacks a `messages` key.","commonSituations":"Registering a hand-rolled LangGraph graph as a subagent with a custom state schema (e.g. only `input`/`output` keys); graphs returning `None` or a state update without messages.","solutions":["Add `messages` to the custom graph's state schema (e.g. via `MessagesState` or an `Annotated[list, add_messages]` key)","Ensure the graph's final node writes to `messages`","If wrapping an existing graph, add a terminal node that appends the result to `messages`"],"exampleFix":"// before\nclass State(TypedDict):\n    result: str\n// after\nclass State(TypedDict):\n    messages: Annotated[list, add_messages]\n    result: str","handlingStrategy":"validation","validationCode":"result = subagent_graph.invoke(inputs)\nif \"messages\" not in result:\n    raise ValueError(\"custom subagent graph must emit 'messages' in final state\")","typeGuard":"def emits_messages(state: dict) -> bool:\n    return isinstance(state, dict) and \"messages\" in state","tryCatchPattern":"try:\n    cmd = task(runtime, description=..., subagent_type=\"custom_graph\")\nexcept ValueError as e:\n    logger.error(\"Subagent state invalid: %s\", e)\n    raise","preventionTips":["Base custom subagent state schemas on `MessagesState` or include `Annotated[list, add_messages]`","Integration-test each custom subagent graph end-to-end through `task`","Add a schema lint that asserts `messages` is in every registered subagent's state schema"],"tags":["python","langgraph","subagents","state-schema"],"backgroundTag":"missing-state-key","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}