Agent Tool Call Errors: Know What Failed Before Retrying
Handle agent tool call errors by separating invalid requests, execution failures, and unknown outcomes. Recover safely without repeating external effects.
Agent tool call errors are not one kind of failure. A malformed argument, a permission denial, and a timeout after a remote write require different responses. Classify both the failure and what is known about its effects before choosing a recovery action. Otherwise, a harness can turn an ordinary interruption into a duplicate external operation.
This is a design guide, not a benchmark or an incident report. The examples below are illustrative. The goal is a recovery policy that can explain why it repaired an argument, waited, reconciled an outcome, or stopped.
Separate tool execution from task completion
A useful tool boundary tracks three questions independently:
- Was the request accepted? The name, arguments, identity, and permissions passed the relevant checks.
- What happened during execution? The operation completed, failed, remained pending, partially applied, or became uncertain to the caller.
- Was the intended outcome verified? Evidence shows that the resulting state satisfies the task.
These are not interchangeable success flags. A valid request can fail a business constraint. A successful write can return through a broken connection. A well-formed response can describe the wrong resource.
The MCP tools specification, revision 2025-06-18, distinguishes protocol errors from tool execution errors reported with isError: true. That distinction belongs in the adapter, but neither channel alone establishes whether a side effect occurred. A server can encounter an error after completing part of its work.
The harness therefore needs a domain contract beyond the protocol envelope. Our guide to MCP and tool-protocol governance describes that boundary: a shared call format does not replace authority checks, versioned semantics, or verification.
Classify agent tool call errors by recovery action
The following taxonomy is a proposed harness policy, not a universal mapping from HTTP status codes. Classify from structured evidence and the tool’s documented behavior; do not infer certainty from an error message alone.
| Failure class | Evidence to establish | Recovery direction |
|---|---|---|
| Invalid request | Validation rejected the request before execution | Repair the named fields; preserve task intent |
| Authorization failure | The required operation is outside current authority | Stop the action; use an authorized escalation path |
| Transient dependency failure | The dependency may recover; effect status is separately known | Apply a bounded retry only when repetition is safe |
| State conflict | The operation depended on an outdated version or unmet precondition | Read current state, then revalidate the proposed change |
| Unknown outcome | No authoritative terminal result was received | Reconcile before repeating an unsafe effect |
| Partial completion | Some sub-operations applied and others did not | Recover per sub-operation; do not replay the whole batch blindly |
| Verification failure | Execution returned, but the outcome check failed or was unavailable | Diagnose the verifier and resulting state before another write |
A timeout is a transport observation, not proof of cancellation. A permission denial is not an invitation to search for a more powerful credential. A version conflict does not mean that overwriting the new version will satisfy the original task.
If an adapter cannot classify the outcome reliably, preserve unknown. Invented certainty is worse than a controlled pause.
Return an error contract the harness can route
Prefer a small structured error record to a paragraph that the model must interpret. This illustrative record is an application-level envelope, not an MCP wire message:
{
"operation_id": "ticket:create:case-8472",
"attempt_id": "attempt-03",
"error_class": "transport_timeout",
"effect_status": "unknown",
"safe_message": "No terminal response received before the deadline.",
"recovery_action": "reconcile",
"evidence_ref": "trace-8472-03"
}
Keep the operation identity stable across recovery; give each attempt its own identity. Persist the intended payload and its digest outside the conversation so a restart cannot silently change the requested effect. Make error classes and effect states constrained fields, and document which component is allowed to assert each value.
The adapter can report an observation. The harness should validate any recommended recovery action against its own policy. A tool’s returned text cannot grant permission, raise a retry budget, or authorize an unrelated operation.
Give the model enough sanitized detail to repair a request: which field failed, the expected type, and the relevant constraint. Keep credentials, private response bodies, and irrelevant stack traces out of its context. Store restricted diagnostic evidence separately when operationally necessary.
Unknown outcomes need reconciliation, not optimism
Consider an illustrative ticket-creation call. The service creates the ticket, but the connection closes before the caller receives its identifier. The harness knows only that its wait ended. Sending a fresh create request could produce another ticket.
The safe next step depends on the service contract. Query an authoritative operation endpoint using the persisted intent key, or replay the same request under a documented idempotency guarantee. A key is useful only if the receiver actually enforces it, the payload is unchanged, and the guarantee still applies within its retention window.
This is not peculiar to agents. HTTP Semantics, RFC 9110 section 9.2.2, says clients should not automatically retry non-idempotent requests unless they know the semantics are idempotent or can determine that the original request was never applied.
A search returning no matching ticket may be insufficient evidence: the search index could lag behind the write. Reconcile against the system that owns the operation. If it cannot establish the outcome, keep the operation unresolved and escalate with its identity and evidence. Do not turn an expired waiting period into permission to duplicate it.
The implementation details belong in the idempotency and safe-rerun contract: durable identity, payload matching, owner-side deduplication, and explicit unknown states.
Retry policies need separate budgets
Argument repair, transport retry, reconciliation polling, and task replanning are different activities. Give them separate limits under one overall deadline and cost ceiling. Otherwise, a small retry limit in each layer can still produce a large amount of work.
For temporary dependency failures, honor applicable server retry guidance and use bounded backoff with jitter. Before dispatching again, check that the operation remains authorized, the deadline has not passed, and repetition is safe. A recovered connection does not answer the last question.
For invalid arguments, a repair must change the rejected part without inventing missing business facts. For a state conflict, refresh the state and reconsider whether the original change still makes sense. For a permission failure, record the block rather than repeatedly presenting the same denied action.
Assign one layer ownership of retries, or make SDK, gateway, and harness retries visible to a shared budget. Count actual network attempts, not just model-visible tool invocations. A model should not need another reasoning turn merely to wait for a rate-limit window.
Test the recovery path, not just the error message
Before shipping a consequential tool, inject failures at the boundaries that matter. These are proposed tests, not claimed results:
- Reject invalid input before dispatch; assert that no external mutation occurs.
- Commit a write and drop its response; assert that recovery finds or safely replays the same operation, without creating another effect.
- Let one item in a batch succeed and another fail; assert that recovery preserves per-item outcomes.
- Fail the verifier after a successful write; assert that the next action inspects state rather than automatically rewriting it.
- Restart the worker during reconciliation; assert that the persisted operation identity and unresolved state survive.
Record the original request reference, normalized failure class, effect status, retry decision, and final verification evidence. Track unresolved operations and duplicate effects alongside eventual completion; a run that finishes after producing two tickets is not a clean success. These failures can then become behavior-verified agent regression gates.
Takeaway
An error tells the harness that something needs attention. It does not, by itself, tell the harness to try again. Separate request validity, execution outcome, and verified completion. Repair what is repairable, retry only under a safe contract, and keep uncertain effects visible until evidence resolves them.