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

  1. Add `messages` to the custom graph's state schema (e.g. via `MessagesState` or an `Annotated[list, add_messages]` key)
  2. Ensure the graph's final node writes to `messages`
  3. 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

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


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/205620b0d3706770. Report an issue: GitHub.