{"record":{"id":"638f1e0b7efea4f4","repo":"langchain-ai/deepagents","slug":"auto-mode-requires-every-proposed-tool-call-to-hav","errorCode":null,"errorMessage":"Auto mode requires every proposed tool call to have an ID","messagePattern":"Auto mode requires every proposed tool call to have an ID","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/auto_mode.py","lineNumber":1646,"sourceCode":")\n\n\ndef _tool_call_id(call: ToolCall) -> str:\n    \"\"\"Return a non-empty tool-call ID.\n\n    Args:\n        call: Proposed tool call.\n\n    Returns:\n        Valid identifier used for plans and decisions.\n\n    Raises:\n        ValueError: If the model omitted a stable identifier.\n    \"\"\"\n    value = call.get(\"id\")\n    if not isinstance(value, str) or not value:\n        msg = \"Auto mode requires every proposed tool call to have an ID\"\n        raise ValueError(msg)\n    return value\n\n\ndef _validate_unique_tool_call_ids(calls: Sequence[ToolCall]) -> None:\n    ids = [_tool_call_id(call) for call in calls]\n    if len(ids) != len(set(ids)):\n        msg = \"Auto mode rejects action batches with duplicate tool-call IDs\"\n        raise ValueError(msg)\n\n\ndef _batch_id(calls: Sequence[ToolCall]) -> str:\n    encoded = \"\\0\".join(_tool_call_id(call) for call in calls).encode(\"utf-8\")\n    return sha256(encoded).hexdigest()\n\n\ndef _review_tool_call_ids(\n    raw_plan: object,\n    valid_tool_call_ids: Collection[str],","sourceCodeStart":1628,"sourceCodeEnd":1664,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/auto_mode.py#L1628-L1664","documentation":"Raised by `_tool_call_id` when a proposed tool call lacks a non-empty string `id`. Auto mode keys human decisions, dedup checks, and batch summaries off these IDs, so it refuses to process any model-proposed action whose call has no stable identifier.","triggerScenarios":"A model (or a hand-constructed message) emits an assistant message with `tool_calls` entries missing `id` or carrying an empty/non-string `id`, and the auto-mode middleware's `awrap_model_call` (via `_validate_unique_tool_call_ids`, `_classifier_context`, `_same_turn_user_answers`, `_batch_id`, or `_managed_temp_rejection`) reads it.","commonSituations":"Using a provider/model that omits tool-call IDs (some proxies or local runtimes strip them); hand-crafting assistant messages in tests without ids; a broken provider integration or streaming bug that drops the id field; resuming a conversation whose history was rewritten and lost ids.","solutions":["Regenerate with a different model or provider that emits proper tool-call IDs, or upgrade the provider integration to a version that preserves them","If a proxy/gateway is in front of the model, configure/upgrade it to pass through the `id` field of tool calls","When constructing tool-call messages manually, always set a unique non-empty `id` string per call","Check message-rewriting middleware in your graph — ensure it does not strip `tool_calls[*].id`"],"exampleFix":"// before: hand-built call missing id\n{\"name\": \"read_file\", \"args\": {\"path\": \"a.py\"}}\n\n// after\n{\"id\": \"call_abc123\", \"name\": \"read_file\", \"args\": {\"path\": \"a.py\"}}","handlingStrategy":"type-guard","validationCode":"from collections.abc import Sequence\n\ndef all_calls_have_ids(calls: Sequence[dict]) -> bool:\n    return all(isinstance(c.get(\"id\"), str) and c.get(\"id\") for c in calls)","typeGuard":"def has_tool_call_id(call: dict) -> bool:\n    return isinstance(call.get(\"id\"), str) and bool(call[\"id\"])","tryCatchPattern":"try:\n    # auto-mode middleware runs here\n    result = agent.invoke(input, config)\nexcept ValueError as exc:\n    if \"every proposed tool call to have an ID\" in str(exc):\n        regenerate_or_reinject_ids_and_retry()\n    else:\n        raise","preventionTips":["Use providers/integrations that reliably emit tool-call IDs; keep them upgraded","Always set a unique non-empty `id` when constructing tool_calls manually","Audit proxies/gateways for field stripping","Check custom message-rewriting middleware preserves tool_calls[*].id"],"tags":["auto-mode","tool-call","validation","model-output"],"backgroundTag":"missing-tool-call-id","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}