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

  1. Regenerate with a different model or provider that emits proper tool-call IDs, or upgrade the provider integration to a version that preserves them
  2. If a proxy/gateway is in front of the model, configure/upgrade it to pass through the `id` field of tool calls
  3. When constructing tool-call messages manually, always set a unique non-empty `id` string per call
  4. 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

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


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