AI Agents, Evergreen

How AI Agents Handle Errors and Edge Cases

Cartoon robot reaching for a red CHARGE button while a human hand holds it back at a safety gate — stop before it charges.

AI agent error handling is the difference between a demo that works and a system that survives its thousandth run. An agent that books a meeting or processes a refund will eventually hit a tool that times out, a model that returns malformed JSON, or an instruction that means two things at once. What happens next — whether the agent recovers quietly, asks a person, or quietly makes things worse — depends heavily on the error-handling design around the model as well as on the model itself. This guide explains how AI agents handle errors and edge cases: how failures are detected and classified, which recovery fits each class, and where autonomous recovery ends and human judgment begins.

Watch the video explainer

The stakes are concrete: in McKinsey’s 2025 survey, inaccuracy was the most common negative consequence of AI use reported by respondents, at 30%.

What handling an error means for an AI agent

An agent error is any step whose result cannot be trusted to advance the goal. A tool can return the wrong data in a 200 response. A model can produce JSON with a missing brace, an argument that does not exist on the tool schema, or a function name it invented. An instruction can be underspecified, a safety policy can refuse the action, or the agent can drift away from the original goal.

The first job is classification, not recovery, because the right response depends on the failure class. LangChain’s fault-tolerance documentation makes the split explicit: transient failures are retried automatically; errors the model can recover from are fed back to the model; and errors that need human input pause the agent. Three classes, three handlers — one generic retry loop cannot do the job.

A qualitative analysis of more than 900 agent traces identified four recurring failure archetypes: premature action without grounding, over-helpfulness under uncertainty, susceptibility to context pollution, and fragile execution under cognitive load. The loop around the model is an important source of those failures — and they compound: a chatbot’s bad answer ends the turn, but an agent’s bad step triggers further actions, so a wrong lookup becomes a wrong refund.

The AI agent error handling loop: detect, classify, decide, act

Cartoon robot walking a four-station loop: magnifying glass, sorting bins, a fork in the path, and a checklist clipboard.

Every recovery follows the same four-step loop.

Detect. Some failures announce themselves — an HTTP error, a schema validation failure, a timeout. Others have to be noticed: an output that contradicts the plan, or the same step repeated identically three times. Anthropic’s guidance on building effective agents makes this a requirement: agents must gain ground truth from the environment at each step, through tool results or code execution, to assess their progress.

Classify. Is it transient or permanent, recoverable by the model or fatal, safe to retry or in need of a person? Anthropic’s API documentation shows why classification must come from the provider’s contract, not from a status code alone. A 429 rate limit error means wait and retry, respecting the retry-after header — but a 429 with the spend-limit error code means the monthly cap was hit, so the run should stop and tell a person. A 500 is retried with backoff; a 504 on a side-effecting tool call must be reconciled first.

Decide. The menu is short: retry, repair, replan, or escalate. Retry fits transient failures; repair fits failures the model can fix with new information; replan fits a wrong approach; escalate fits everything else:

# Fragment: decision logic, not a runnable program.
# The error object and the retry/escalate helpers come from the agent framework.
def handle_failure(error):
    failure_class = classify(error)          # tool_failure, invalid_output, ...
    if failure_class == "transient" and attempts_left(error) > 0:
        if not safe_to_repeat(error):        # unknown write outcome: reconcile first
            return reconcile_or_escalate(error)
        wait_with_backoff(error)
        return retry(error)
    if failure_class == "llm_recoverable":
        return feed_back_to_model(error)     # parser message becomes the next observation
    return escalate_to_human(error)          # pause the run, keep the state

Act and record. Whatever is chosen, the harness logs the failure, the classification, and the chosen response — the audit trail.

Retries that actually help

Cartoon robot climbing three clocks of increasing size with a retry arrow, hourglass and budget meter nearby.

Retrying is a common recovery and an easy one to misapply. Three rules keep it honest. First, retries must be bounded. The Anthropic SDK retries transient failures twice by default — an initial attempt plus two retries — and LangChain’s tool-retry middleware defaults to three total attempts, an initial attempt plus two retries. The application chooses its own maximum according to its budget and recovery policy; neither default is a recommended maximum. A fixed bound means a failing step cannot loop forever.

Second, how you wait matters. The standard technique is exponential backoff — wait a short time, then twice as long, then twice as long again — and when a 429 includes a retry-after header, use it as a minimum. The SDKs already do much of this:

// Fragment of an Anthropic client setup (Node.js SDK).
const client = new Anthropic({
  timeout: 60000,    // 60-second request timeout
  maxRetries: 3,     // automatic retries on transient failures
});

Third, change something on retry — when the failure is one that repeating the same request cannot fix. When a model produces invalid JSON, it often repeats the same mistake, so the retry needs a different prompt, a smaller step, a fallback tool, or the parser’s error message fed back into context. That requirement does not apply to transient failures: an identical request can succeed after a network fault clears or a rate limit lifts, with waiting as the only change. The arithmetic shows why: suppose each attempt succeeds with probability one half, independently of the others. An initial attempt plus three retries is four attempts, so the success probability is 1 minus 0.5 to the fourth power, or 93.75% — and conditional on the initial attempt already having failed, the three retries alone give 1 minus 0.125, or 87.5%. For a deterministic failure such as a malformed request or a spend cap, the per-attempt probability is near zero, so success stays near zero while attempts go to four — quadrupling cost and latency for nothing. That is illustrative arithmetic, not a measured result: the trials are assumed independent, persistent overload creates correlated failures, and no measured production recovery rate is supplied. Some failures should never consume a retry: a 400 means the request itself is the problem, and a policy refusal or spend cap is a decision, not a glitch.

Validation and self-checks that catch bad output

The cheapest error is the one caught before it acts. Structured outputs handle the shape: OpenAI’s Structured Outputs, launched in August 2024, ensure the model will always generate responses that adhere to the supplied JSON Schema, enforced by constrained decoding. The older JSON mode only guarantees valid JSON, not schema adherence. A schema for an error-classification step can force the model to return one of a fixed set of failure classes and one of a fixed set of actions:

{
  "type": "object",
  "properties": {
    "failure_class": {
      "type": "string",
      "enum": ["tool_failure", "invalid_output", "ambiguous_input", "policy_block", "state_drift"]
    },
    "retryable": { "type": "boolean" },
    "action": {
      "type": "string",
      "enum": ["retry", "repair", "replan", "escalate"]
    }
  },
  "required": ["failure_class", "retryable", "action"],
  "additionalProperties": false
}

But a schema guarantees the shape of the answer, not that it is right: OpenAI states plainly that structured outputs can still contain mistakes. Refusals surface in a dedicated refusal field, and a response that hits the token limit can be truncated so it no longer matches the schema. The schema is the first layer, not the whole solution — the second is verification in code. Before a consequential action, the agent re-reads the tool result it is about to act on, checks preconditions, and compares the output against the plan. Anthropic’s evaluator-optimizer workflow formalises this as programmatic gates on intermediate steps.

Edge cases: ambiguity, partial success, and loops

Clean failures are the easy half of the problem. Ambiguous instructions come first: when the request can be read two ways, the agent should clarify or fall back to a narrower default, never guess silently on a consequential action. Over-helpfulness under uncertainty is one of the four failure archetypes from the 900-trace analysis.

Conflicting tool results need a precedence rule decided in advance, and the conflict must be recorded rather than silently resolved.

Partial success is the hardest to get right: three emails sent, the fourth bounced. Restarting from scratch would duplicate the three that succeeded, so the run must resume from the last completed step, using persisted state for the steps already done — which only works if the operations that may be replayed are idempotent. LangGraph’s interrupt mechanism re-runs a node from its start on resume, so anything before the pause that writes, sends, or charges must be safe to repeat or moved after the pause. Moving an operation after the pause avoids repeating it altogether, and an uncertain outcome still requires reconciliation before any retry.

Loops and repetition are the final trap: an agent that retries the same failing step or keeps choosing the same wrong tool is stuck, burning budget while looking busy. The defences are step-count caps and progress checks. A useful policy — chosen by the application, not a universal rule — is to treat two identical failures as the signal: the third attempt should be a different approach or an escalation.

When to stop and escalate to a human

Escalation is not failure; it is the designed endpoint for the failures the agent cannot resolve on its own. The boundary is drawn by risk. Low-risk work can retry automatically and report afterwards; consequential or irreversible actions — a payment, a deletion, a message to a customer — need a person before they happen.

OpenAI’s Agents SDK implements this as guardrails plus human review: input guardrails validate before the main model runs, tool guardrails check arguments or results around a function call, and human review pauses the run, returning the pending approvals and a resumable run state. Approval rules fail closed: if the arguments cannot be safely inspected, the call goes to a person. Anthropic’s guidance recommends that agents pause for human feedback at checkpoints or when encountering blockers. The approval boundary for consequential actions is this article’s design policy, following the brief’s risk-tiered approach; LangChain’s interrupt mechanism pauses the agent before specific tool calls.

The signals are concrete: the same step has failed repeatedly, confidence is low, the request hits a policy boundary, or a budget has been exceeded. A good handoff contains four things: what was tried, what failed, the state so far, and the decision needed. In a September 2026 follow-up, 80% of the 40 organisations that let agents skip human review for some low-risk changes reported an agent that passed internal evaluations and then failed in front of a customer, against 36% of the 36 organisations that required review — a correlation, not proof of causation.

Designing for failure from the start

Four practices cover most of it. Guardrails first: least-privilege tool access scoped to the specific task, each agent with its own identity so its actions can be traced, step caps and time and cost budgets so no run can spiral.

Idempotency everywhere it matters: every tool call that changes something should be safe to repeat, keyed by an idempotency key where the API supports one. Actions that cannot be retried or reversed need a compensating action — the saga pattern — or a human approval in front of them. An unknown outcome is not a failed action: stop, preserve the evidence, and escalate instead of guessing.

Logging and observability: every step, tool call, failure, and decision recorded and reviewable, with the error type and request identifier attached. Record the request identifiers and error metadata the provider returns with each failure — they are what let support find the exact request later.

Test the failure, not just the success: deliberately inject tool failures and bad outputs in staging. The τ-bench results show why: GPT-4o succeeded on about 61% of retail tasks in a single attempt, but only about 25% of tasks in all eight repeated attempts. Succeeding at least once in k tries is fine when retrying is acceptable; succeeding on every one of k tries is what a business process needs.

Common mistakes

Unbounded retries burn budget and time on a deterministic failure. Silent fallback — the agent quietly doing something different instead of reporting the failure — turns a recoverable error into an undetected wrong outcome. Treating every error the same way wastes the classification work: a spend cap is not a rate limit, and a 400 is not a 500. No audit trail means the same failure recurs invisibly. Escalating everything produces alert fatigue; escalating nothing produces silent damage.

The pattern underneath all of them is the same: the agent was built for the happy path, and the unhappy path was left to the model to improvise. AI agent error handling is the work of deciding in advance what happens when each kind of thing goes wrong — detect it, classify it, choose from a short menu of responses, and know exactly when to stop and ask a person. Build that loop into the harness and test it with real failures, and defined failures become easier to detect, contain, and escalate. Testing cannot establish coverage of every edge case, so unforeseen failures remain possible even in a system built this way.