langchain-ai/deepagents · error · ValueError
Classifier result did not contain exactly one decision per r
Error message
Classifier result did not contain exactly one decision per reviewed call
What it means
When Auto Mode delegates review of a batch of gated tool calls to a classifier, the classifier must return exactly one decision per reviewed call, with no missing, duplicated, or unknown tool_call_ids. `_validate_classifier_ids` (called from `awrap_model_call`) enforces this by comparing the decision ids against the expected id set. Any mismatch means the classifier output is unusable, so the middleware fails fast with this ValueError.
Source
Thrown at libs/code/deepagents_code/auto_mode.py:2101
if isinstance(value, str) and value:
return value
return type(model).__name__
def _validate_classifier_ids(batch: AutoDecisionBatch, expected_ids: set[str]) -> None:
"""Validate exact one-to-one classifier coverage.
Args:
batch: Structured classifier result.
expected_ids: Tool-call IDs requiring model review.
Raises:
ValueError: If IDs are missing, duplicated, or unknown.
"""
actual_ids = [decision.tool_call_id for decision in batch.decisions]
if len(actual_ids) != len(set(actual_ids)) or set(actual_ids) != expected_ids:
msg = "Classifier result did not contain exactly one decision per reviewed call"
raise ValueError(msg)
class AutoModeHITLMiddleware(HumanInTheLoopMiddleware[AutoModeState, Any, Any]):
"""Apply deterministic policy, classifier review, and HITL fallback."""
trace_policy = TracePolicy(process_inputs=omit_payload)
"""Omit hook inputs from traces by default; set a `TracePolicy` to override."""
state_schema = AutoModeState
@property
def name(self) -> str:
"""Replace the stock main-agent HITL middleware by name."""
return "HumanInTheLoopMiddleware"
def __init__(
self,
interrupt_on: Mapping[str, bool | InterruptOnConfig],View on GitHub (pinned to a1af029e6e)
Solutions
- Use a structured-output-capable classifier model or stricter response schema so every reviewed tool_call_id gets exactly one decision.
- Retry the classifier with a prompt that emphasizes returning one decision per call id, verbatim from the request.
- For long batches, split review into smaller chunks so the classifier cannot truncate decisions.
- Log the classifier's raw output to identify which ids were missing, duplicated, or unknown.
Example fix
// before
classifier_response = '{"decisions": [{"tool_call_id": "call_1", "decision": "approve"}]}' // call_2 missing
// after
classifier_response = '{"decisions": [{"tool_call_id": "call_1", "decision": "approve"}, {"tool_call_id": "call_2", "decision": "reject"}]}' Defensive patterns
Strategy: validation
Validate before calling
expected = {c['id'] for c in batch.tool_calls}
actual = [d.tool_call_id for d in batch.decisions]
if len(actual) != len(set(actual)) or set(actual) != expected:
# retry the classifier before consuming results
batch = rerun_classifier(batch.tool_calls) Type guard
def classifier_batch_is_complete(decisions: list[Decision], expected_ids: set[str]) -> bool:
ids = [d.tool_call_id for d in decisions]
return len(ids) == len(set(ids)) and set(ids) == expected_ids Try / catch
try:
validated = middleware_validate(batch)
except ValueError as e:
if 'exactly one decision per reviewed call' in str(e):
batch = retry_classifier_with_stricter_prompt(batch) # or fall back to HITL review
else:
raise Prevention
- Use a model with structured-output support for classification and validate the response schema before the middleware does.
- Keep review batches small enough that the classifier cannot truncate output.
- Log raw classifier output so missing/duplicated/unknown ids can be diagnosed quickly.
- Include verbatim tool_call_ids in the classifier prompt and forbid the model from inventing ids.
When it happens
Trigger: The classifier model returns a batch whose decisions contain: a tool_call_id not in the reviewed batch, a duplicated id (len(actual_ids) != len(set(actual_ids))), or omits an id present in the batch — typically because the classifier LLM truncated, hallucinated, or reformatted ids.
Common situations: A weak/small classifier model emitting free-text instead of the expected structured decisions; prompt changes that drop the tool_call_id field; long batches where the model truncates output; classifier response parsing that mangles ids.
Related errors
- 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
- Auto mode rejects action batches with duplicate tool-call ID
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/8c17afb4cd0811f7.
Report an issue: GitHub.