{"record":{"id":"ad43ddcb17dac1af","repo":"langchain-ai/deepagents","slug":"subagent-spec-name-must-specify-tools","errorCode":null,"errorMessage":"SubAgent '{spec['name']}' must specify 'tools'","messagePattern":"SubAgent '(.+?)' must specify 'tools'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/middleware/subagents.py","lineNumber":365,"sourceCode":"    Args:\n        spec: Subagent spec to compile. Must specify `model` and `tools`.\n        state_schema: Base graph state schema forwarded to `create_agent` for\n            the subagent.\n        response_format: Optional response format override for this compiled\n            subagent instance.\n\n    Returns:\n        Runnable agent ready for task-tool invocation.\n\n    Raises:\n        ValueError: If `spec` is missing `model` or `tools`.\n    \"\"\"\n    if \"model\" not in spec:\n        msg = f\"SubAgent '{spec['name']}' must specify 'model'\"\n        raise ValueError(msg)\n    if \"tools\" not in spec:\n        msg = f\"SubAgent '{spec['name']}' must specify 'tools'\"\n        raise ValueError(msg)\n\n    from deepagents._models import resolve_model  # noqa: PLC0415\n\n    model = resolve_model(spec[\"model\"])\n    middleware: list[AgentMiddleware] = list(spec.get(\"middleware\", []))\n\n    interrupt_on = spec.get(\"interrupt_on\")\n    if interrupt_on:\n        middleware.append(HumanInTheLoopMiddleware(interrupt_on=interrupt_on))\n\n    selected_response_format = response_format if response_format is not None else spec.get(\"response_format\")\n    create_agent_kwargs: dict[str, Any] = {\n        \"system_prompt\": spec[\"system_prompt\"],\n        \"tools\": spec[\"tools\"],\n        \"middleware\": middleware,\n        \"name\": spec[\"name\"],\n        \"response_format\": selected_response_format,\n    }","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/middleware/subagents.py#L347-L383","documentation":"Like `model`, a `SubAgent` spec must declare `tools`. If the `tools` key is missing, `create_sub_agent` raises `ValueError`. The library requires the key to be present (even an empty list) so intent is explicit rather than inferred.","triggerScenarios":"Calling `create_sub_agent(spec=...)` with a dict containing `name` and `model` but no `tools` key.","commonSituations":"Authoring read-only/reasoning subagents and assuming tools are optional; YAML/JSON configs where the tools list was omitted; typos like `tool` instead of `tools`.","solutions":["Add a `tools` key; use `[]` for a tools-free subagent","List the intended tool callables under `tools`","Check for key typos and that config loading doesn't drop empty lists"],"exampleFix":"// before\n{\"name\": \"planner\", \"model\": \"openai:gpt-4.1\"}\n// after\n{\"name\": \"planner\", \"model\": \"openai:gpt-4.1\", \"tools\": []}","handlingStrategy":"validation","validationCode":"if \"tools\" not in spec:\n    raise ValueError(f\"spec {spec.get('name')!r} missing 'tools'; use [] for none\")","typeGuard":"def has_tools_key(spec: dict) -> bool:\n    return \"tools\" in spec","tryCatchPattern":"try:\n    agent = create_sub_agent(spec=spec)\nexcept ValueError as e:\n    logger.error(\"Subagent spec needs tools: %s\", e)\n    raise","preventionTips":["Always include an explicit `tools` key, even when empty","Use a Pydantic/TypedDict schema for specs with `tools: list` required","Snapshot-test spec builders against the required-key set"],"tags":["python","subagents","validation","valueerror"],"backgroundTag":"missing-required-field","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}