Agent architecture

Exception handling that makes agents safe to leave alone

Autonomous agents fail in ways code never did — ambiguous inputs, half-applied side effects, low-confidence decisions. We engineer the failure paths first: typed exceptions, retry and compensation policies, clean escalation, and dead-letter queues that turn every edge case into an auditable event.

  • Typed exception taxonomy
  • Retry, compensate, or escalate
  • Dead-letter queues with full context
  • Human escalation that closes the loop
70%+
of production incidents live in the edge cases, not the happy path
0
silent failures — every exception is captured and routed
<1
irreversible action without a gate or compensation handler
100%
of escalations carry replayable context
// the engineering decision

An agent's failure modes are not your code's failure modes

Deterministic software fails predictably; you catch a thrown error and roll back. An agent fails probabilistically — it can misread a field, pick the wrong tool, or be quietly half-right.

A traditional try/catch assumes the failure is the exception. With agents, the dangerous case is the one that never throws: the model confidently completes a task using the wrong assumption, and your systems happily record the result. There's no stack trace for being wrong.

So exception handling for agents is really three problems stacked together. First, detection — knowing the agent is off-track when it doesn't error. Second, classification — deciding whether a given failure is retryable, compensable, or out of scope. Third, disposition — routing it to a retry, a compensation handler, a human, or a dead-letter queue, with enough context to act. Get this layer right and autonomy becomes a question of trust budget rather than a leap of faith.

// the failure-handling layer

Five mechanisms, one safety net

Each agent action passes through a governed handler that decides what happens when reality doesn't match the plan.

// the disposition path

What happens when an action fails

A single, predictable flow every agent action runs through — no improvisation at the edge.

01

Detect

Output validators, confidence scores, and invariants flag the failure — including the ones that don't throw an error.

02

Classify

The handler tags the failure by type: transient, validation, policy boundary, low-confidence, or irreversible.

03

Route

Retry the transient ones, compensate the partial work, escalate the ambiguous ones, or trip a breaker on systemic faults.

04

Record

Every disposition writes to the DLQ and the decision lineage, so the next version learns from the edge case.

// the hard tradeoff

Fail loud, or fail autonomous?

There's a real tension here. Escalate too eagerly and you've built an expensive notification system that wakes people up for noise — the agent never earns its autonomy. Retry and self-heal too aggressively and the agent papers over genuine problems, compounding bad state until someone notices the damage downstream.

We tune that boundary deliberately, per workflow, using the same risk thresholds that gate execution. Low-stakes, reversible steps get a wide self-healing budget. Irreversible or high-cost steps fail loud and escalate early. The taxonomy is what lets you set this dial precisely instead of choosing one blunt policy for the whole system.

  • Per-workflow retry and escalation budgets
  • Risk thresholds drive the self-heal vs. escalate dial
  • Abstention treated as a valid, cheap outcome

Naive try/catch vs. agent exception handling

Why bolting traditional error handling onto an agent leaves the dangerous failures uncovered.

Wrapping the agent in try/catchA governed failure-handling layer
DetectsOnly failures that throwThrows plus low-confidence and silent-wrong outputs
Retry logicBlind retry until budget runs outRetries only transient, idempotent failures
Partial workLeft half-appliedCompleted or reversed via compensation
Unresolved casesLogged and lostDead-lettered with replayable context
Human handoffA vague alertEscalation with full decision lineage
LearningNoneEdge cases feed the next version's guardrails

Frequently asked questions

What's the difference between a retry and an escalation?

A retry is appropriate when the failure is transient and idempotent — a timeout, a rate limit, a 503. An escalation is for failures the agent can't or shouldn't resolve alone: ambiguous data, a policy boundary, or anything that crosses a risk threshold. The hard engineering work is classifying which is which at runtime, not retrying blindly until you exhaust a budget.

Where do failed actions go when the agent gives up?

Into a dead-letter queue with the full context attached — inputs, the plan, every tool call, the model's reasoning, and the exception type. Nothing silently disappears. A human or a triage agent can replay, repair, or close the item, and the resolution feeds back into the next version's guardrails.

How do you keep a half-finished workflow from corrupting state?

We design side-effecting steps to be idempotent or compensable. If an agent books a vendor but fails to record it, a compensation handler either completes or reverses the partial work — the same saga pattern distributed systems use. Approval gates sit in front of the irreversible steps so a human signs off before the point of no return.

Can the agent decide to stop on its own?

Yes, and it should. We give agents explicit 'I'm not confident' and 'this is out of policy' exits so abstention is a first-class outcome, not a fallback after three bad attempts. An agent that halts cleanly and escalates is far cheaper than one that confidently completes the wrong work.

Related architecture decisions

Exception handling is one layer of a governed agent stack. Explore the rest.

Bring the workflow that scares you. We'll map its failure paths.

One working session to chart where your agent can fail, what's reversible, and where a human needs to stand in the loop.