langchain-ai/deepagents · error · ValueError
Human decision count does not match Manual pending calls
Error message
Human decision count does not match Manual pending calls
What it means
Raised by `_validate_human_decision_count` inside `_human_review` when the number of human decisions supplied for a pending approval or Manual batch does not equal the number of pending tool calls in that batch. Auto mode requires a one-to-one decision per pending call to construct valid `ToolMessage` results; the sibling message 'does not match pending approval calls' is used when not in Manual mode.
Source
Thrown at libs/code/deepagents_code/auto_mode.py:1736
return f"untrusted-{id(runtime):x}:{_batch_id(calls)}"
return f"{thread_key}:{_batch_id(calls)}"
def _validate_human_decision_count(
decisions: Sequence[object], calls: Sequence[ToolCall], *, manual: bool
) -> None:
"""Reject incomplete human responses before applying their decisions.
Raises:
ValueError: If the response has the wrong number of decisions.
"""
if len(decisions) == len(calls):
return
if manual:
msg = "Human decision count does not match Manual pending calls"
else:
msg = "Human decision count does not match pending approval calls"
raise ValueError(msg)
def _resolved_tools(request: ModelRequest) -> dict[str, BaseTool]:
return {
tool.name: tool
for tool in request.tools
if isinstance(tool, BaseTool) and isinstance(tool.name, str)
}
def _resolve_path(root: Path, raw: object) -> Path | None:
"""Return the absolute path a model-authored path argument names.
The argument is untrusted model output, so expansion is part of what can
fail: `Path.expanduser` raises `RuntimeError` for a `~name` prefix that
names no account on this host. Expansion runs inside the guard for that
reason, and every failure yields `None`.
View on GitHub (pinned to a1af029e6e)
Solutions
- Re-fetch the current pending calls and resubmit exactly one decision per call in batch order
- Discard stale decision payloads saved from a previous turn and capture fresh decisions against the current batch
- Check the approval frontend for UI-state races that let users submit before the batch renders fully
- If resuming from a checkpoint, regenerate the decision list from the checkpoint's pending calls rather than reusing old data
Example fix
// before: 1 decision for 2 pending calls
decisions = [{"type": "approve"}]
// after: one decision per pending call
decisions = [{"type": "approve"}, {"type": "reject", "reason": "not needed"}] Defensive patterns
Strategy: validation
Validate before calling
def decisions_match_pending(decisions: list, pending_calls: list) -> bool:
return len(decisions) == len(pending_calls) Try / catch
try:
resumed = agent.invoke(Command(resume=decisions), config)
except ValueError as exc:
if "decision count does not match" in str(exc):
decisions = fetch_fresh_pending_and_decide_again()
resumed = agent.invoke(Command(resume=decisions), config)
else:
raise Prevention
- Always derive decisions from the current pending-calls list, never cached state
- Disable or debounce approval UIs until the batch has fully rendered
- On resume, rebuild the decision array from the checkpoint's pending calls
- Add a length assertion in approval frontends before submitting
When it happens
Trigger: Resuming an interrupted turn with a decision list that is shorter or longer than the pending calls — e.g. submitting answers from a stale UI snapshot after the model proposed a different number of calls, replaying a saved receipt against a changed batch, or a frontend dropping decisions for some calls.
Common situations: UI state drift: user approves in one window while the agent re-proposed a different batch; resuming a checkpoint with a hardcoded decisions array; Manual-mode flows where the operator supplies decisions for only some calls; serialization bugs in custom approval frontends.
Related errors
- Human decision count does not match pending approval calls
- tool_call_id must not be empty
- deny decisions require a reason
- trusted thread, turn, and tool-call identity are required
- Auto mode requires every proposed tool call to have an ID
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/38d49846abdaf18d.
Report an issue: GitHub.