langchain-ai/deepagents · error · ValueError
Auto mode requires every proposed tool call to have an ID
Error message
Auto mode requires every proposed tool call to have an ID
What it means
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.
Source
Thrown at libs/code/deepagents_code/auto_mode.py:1646
)
def _tool_call_id(call: ToolCall) -> str:
"""Return a non-empty tool-call ID.
Args:
call: Proposed tool call.
Returns:
Valid identifier used for plans and decisions.
Raises:
ValueError: If the model omitted a stable identifier.
"""
value = call.get("id")
if not isinstance(value, str) or not value:
msg = "Auto mode requires every proposed tool call to have an ID"
raise ValueError(msg)
return value
def _validate_unique_tool_call_ids(calls: Sequence[ToolCall]) -> None:
ids = [_tool_call_id(call) for call in calls]
if len(ids) != len(set(ids)):
msg = "Auto mode rejects action batches with duplicate tool-call IDs"
raise ValueError(msg)
def _batch_id(calls: Sequence[ToolCall]) -> str:
encoded = "\0".join(_tool_call_id(call) for call in calls).encode("utf-8")
return sha256(encoded).hexdigest()
def _review_tool_call_ids(
raw_plan: object,
valid_tool_call_ids: Collection[str],View on GitHub (pinned to a1af029e6e)
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`
Example fix
// before: hand-built call missing id
{"name": "read_file", "args": {"path": "a.py"}}
// after
{"id": "call_abc123", "name": "read_file", "args": {"path": "a.py"}} Defensive patterns
Strategy: type-guard
Validate before calling
from collections.abc import Sequence
def all_calls_have_ids(calls: Sequence[dict]) -> bool:
return all(isinstance(c.get("id"), str) and c.get("id") for c in calls) Type guard
def has_tool_call_id(call: dict) -> bool:
return isinstance(call.get("id"), str) and bool(call["id"]) Try / catch
try:
# auto-mode middleware runs here
result = agent.invoke(input, config)
except ValueError as exc:
if "every proposed tool call to have an ID" in str(exc):
regenerate_or_reinject_ids_and_retry()
else:
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- trusted thread, turn, and tool-call identity are required
- Auto mode rejects action batches with duplicate tool-call ID
- tool_call_id must not be empty
- deny decisions require a reason
- Human decision count does not match Manual pending calls
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/638f1e0b7efea4f4.
Report an issue: GitHub.