{"record":{"id":"73302e1998d7bad7","repo":"langchain-ai/deepagents","slug":"auto-mode-rejects-action-batches-with-duplicate-to","errorCode":null,"errorMessage":"Auto mode rejects action batches with duplicate tool-call IDs","messagePattern":"Auto mode rejects action batches with duplicate tool-call IDs","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/auto_mode.py","lineNumber":1654,"sourceCode":"\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],\n) -> list[str]:\n    \"\"\"Return the reviewed tool-call IDs a checkpointed plan still covers.\n\n    These IDs only pause and resume tool rows in the client, so a malformed\n    value degrades instead of invalidating the plan that carries it: rejecting\n    the plan would discard the classifier's authorization decisions over\n    presentation metadata. Drop anything the current message cannot key, and\n    drop repeats — the client rejects a duplicated ID and falls back to","sourceCodeStart":1636,"sourceCodeEnd":1672,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/auto_mode.py#L1636-L1672","documentation":"Raised by `_validate_unique_tool_call_ids` when a model proposes an action batch containing two or more tool calls with the same `id`. Auto mode needs unique IDs to correlate each proposed action with its own human decision and result message, so it rejects the whole batch.","triggerScenarios":"A model response (or synthesized message) includes `tool_calls` where duplicate `id` values appear — e.g. a buggy model echoing the same id for parallel calls, a provider streaming bug, or test harness code duplicating a call dict.","commonSituations":"Faulty or quantized local models that emit parallel calls with identical ids; provider/proxy bugs collapsing parallel tool-call ids during streaming; middleware that clones a tool call (e.g. to retry) without minting a new id; hand-built test fixtures with copy-pasted call dicts.","solutions":["Regenerate the request — transient model glitches usually clear on retry; consider a stronger model if it recurs","Ensure any middleware that clones or retries tool calls assigns a fresh unique `id` to the copy","Fix test fixtures by giving each constructed call a distinct id (e.g. `call_1`, `call_2`)","If a provider integration systematically duplicates ids, upgrade or report it against the integration"],"exampleFix":"// before\n[{\"id\": \"call_1\", \"name\": \"read\", \"args\": {\"p\": \"a\"}},\n {\"id\": \"call_1\", \"name\": \"read\", \"args\": {\"p\": \"b\"}}]\n\n// after\n[{\"id\": \"call_1\", \"name\": \"read\", \"args\": {\"p\": \"a\"}},\n {\"id\": \"call_2\", \"name\": \"read\", \"args\": {\"p\": \"b\"}}]","handlingStrategy":"validation","validationCode":"from collections.abc import Sequence\n\ndef ids_are_unique(calls: Sequence[dict]) -> bool:\n    ids = [c.get(\"id\") for c in calls]\n    return len(ids) == len(set(ids))","typeGuard":"def has_unique_ids(calls: Sequence[dict]) -> bool:\n    return all(isinstance(c.get(\"id\"), str) for c in calls) and len({c[\"id\"] for c in calls}) == len(calls)","tryCatchPattern":"try:\n    result = agent.invoke(input, config)\nexcept ValueError as exc:\n    if \"duplicate tool-call IDs\" in str(exc):\n        retry_request_or_fix_synthesizing_middleware()\n    else:\n        raise","preventionTips":["When cloning/retrying a tool call in middleware, mint a fresh unique id","Give every constructed call a distinct id in test fixtures","Watch for provider/streaming bugs that duplicate ids; upgrade the integration","Prefer models known to handle parallel tool calls correctly"],"tags":["auto-mode","tool-call","validation","duplicate"],"backgroundTag":"duplicate-tool-call-id","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}