langchain-ai/deepagents · error · ValueError
CompiledSubAgent must return a state containing a 'messages'
Error message
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.
What it means
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`.
Source
Thrown at libs/deepagents/deepagents/middleware/subagents.py:484
subagent_description_str = "\n".join(f"- {s['name']}: {s['description']}" for s in compiled_subagents)
# Use custom description if provided, otherwise use default template
if task_description is None:
description = TASK_TOOL_DESCRIPTION.format(available_agents=subagent_description_str)
elif "{available_agents}" in task_description:
description = task_description.format(available_agents=subagent_description_str)
else:
description = task_description
def _return_command_with_state_update(result: dict, tool_call_id: str) -> Command:
# Validate that the result contains a 'messages' key
if "messages" not in result:
error_msg = (
"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."
)
raise ValueError(error_msg)
state_update = {k: v for k, v in result.items() if k not in _EXCLUDED_STATE_KEYS and k not in private_state_keys}
structured = result.get("structured_response")
if structured is not None:
if hasattr(structured, "model_dump_json"):
content: str = structured.model_dump_json()
elif dataclasses.is_dataclass(structured) and not isinstance(structured, type):
content = json.dumps(dataclasses.asdict(structured))
else:
content = json.dumps(structured)
else:
# Walk back to the last AIMessage with non-empty text. Anthropic
# occasionally emits a trailing empty `end_turn` AIMessage after a
# successful final tool call, which would otherwise be forwarded
# as an empty ToolMessage.
content = ""
for msg in reversed(result["messages"]):View on GitHub (pinned to a1af029e6e)
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`
Example fix
// before
class State(TypedDict):
result: str
// after
class State(TypedDict):
messages: Annotated[list, add_messages]
result: str Defensive patterns
Strategy: validation
Validate before calling
result = subagent_graph.invoke(inputs)
if "messages" not in result:
raise ValueError("custom subagent graph must emit 'messages' in final state") Type guard
def emits_messages(state: dict) -> bool:
return isinstance(state, dict) and "messages" in state Try / catch
try:
cmd = task(runtime, description=..., subagent_type="custom_graph")
except ValueError as e:
logger.error("Subagent state invalid: %s", e)
raise Prevention
- 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
When it happens
Trigger: Invoking a `task` tool whose subagent is a custom `CompiledSubAgent` wrapping a StateGraph whose output state (or state schema) lacks a `messages` key.
Common situations: 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.
Related errors
- StateBackend must be used inside a LangGraph graph execution
- StateBackend requires CONFIG_KEY_READ / CONFIG_KEY_SEND in t
- Async subagent '{name}' has no url configured. ASGI transpor
- Failed to get run status: {e}
- Failed to cancel run: {e}
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/205620b0d3706770.
Report an issue: GitHub.