Fault Recovery in AI Agent Systems: Assume the Model Will Fail Mid-Flight
If your agent cannot survive a crashed worker, a timed-out tool, or a rate-limited model, it is not a product — it is a script with confidence. Production AI systems fail in the middle. The product question is whether failure becomes recoverable progress or silent corruption.
This is one of the least glamorous, most differentiating skills in AI product engineering: designing fault recovery before the first customer depends on the workflow.
Failure is the default timeline
Typical mid-flight failures:
- LLM provider 429 / 500 mid-tool-loop
- Tool succeeds but the process dies before persisting the observation
- Partial CRM update + incomplete follow-up email
- Duplicate retries create duplicate tasks
- Human closes the laptop during a long research run
If your mental model is “retry the whole job,” you will double-charge cards, spam users, and lose trust.
The recovery contract
Before implementation, I write a recovery contract for the feature:
- What is the unit of progress? (step, document, entity, meeting)
- What must be idempotent? (creates, sends, charges)
- What is safe to redo? (pure retrieval, classification)
- What requires human arbitration? (conflicting writes, low-confidence mutations)
- What does the user see while we recover? (status, last good checkpoint)
No contract → no production agent.
Checkpoints beat vibes
Durable execution patterns (Temporal, custom workflow engines, or carefully designed queues) share an idea: persist decisions at boundaries.
A practical agent step boundary:
Load checkpoint
→ Decide next action (recorded)
→ Execute tool with idempotency key
→ Validate observation
→ Reduce into state
→ Persist checkpoint
→ Emit metrics
If you die anywhere after the tool but before checkpoint, replay must detect the tool already ran.
Idempotency keys are product infrastructure
Every side-effecting tool call gets a key derived from:
workflow_id + step_name + logical_input_hash
The tool adapter checks a ledger before executing. Retries become no-ops instead of duplicates.
Idempotency is not an optimization. It is how you make “at least once” delivery compatible with customer trust.
Separate model failure from tool failure
Treat these differently:
| Failure | Recovery stance |
|---|---|
| Model timeout / malformed JSON | Retry with backoff; maybe fall back model; eventually needs_review |
| Tool auth error | Stop; alert; do not burn tokens guessing |
| Tool empty result | Continue with alternate strategy or ask human |
| Tool success, ambiguous meaning | Do not invent; mark uncertain fields |
| Budget exhausted | Checkpoint and pause; never silently truncate critical steps |
A common bug: catching all exceptions and “asking the model what to do.” That turns infrastructure errors into creative writing.
Sagas for multi-write workflows
When a workflow must touch multiple systems (DB + email + CRM), use compensating actions or staged commits:
- Write pending records in your DB
- Perform external mutations with idempotency
- Mark committed only when all required effects succeed
- On failure, compensate or leave pending for human ops
Never let the model be the distributed transaction coordinator. Models are bad at that. Workflow engines and ledgers are good at that.
Human takeover as a first-class state
needs_review is not a shame status. It is how products stay honest.
Design:
- Freeze side effects when confidence or budget thresholds trip
- Show the human the checkpoint, evidence, and proposed next action
- Resume from the same workflow ID — do not restart from zero
- Record the human decision as durable state for evals
This is end-to-end ownership: the system asks for help instead of improvising damage.
Timeouts, heartbeats, and long work
Long agent runs need liveness:
- Heartbeat while waiting on tools
- Soft timeouts per step; hard timeouts per workflow
- Progress events the UI can show (“retrieving sources… validating schema…”)
- Cancellation that leaves a clean checkpoint, not a half-write
Users will wait for async work. They will not wait without a signal.
Poison messages and infinite loops
Agents love loops. Guardrails:
- Max tool calls / max tokens / max wall clock
- Detect repeated identical actions
- Circuit-break a flaky tool after N failures
- Dead-letter workflows that exceed policy for ops inspection
If you only log “agent finished with error,” you will not sleep.
Observability that enables recovery
Logs that say LLMError are useless. I want:
workflow_id,run_id,step,attempt- Model + prompt version hashes
- Tool name + idempotency key + latency
- Checkpoint version
- Cost accrued so far
- Last human-visible status
With that, on-call can answer: resume, compensate, or kill?
A pattern from durable extraction pipelines
For meeting → structured professional memory style systems, Temporal (or equivalent) shines because:
- Audio/transcription/LLM extraction each can fail independently
- Activities are retried with backoff
- Workflow state remembers which stage completed
- Humans can approve derived tasks without reprocessing audio
The product outcome is boring in the best way: yesterday’s meeting still becomes today’s tasks even if a provider blinked.
Testing recovery (do this on purpose)
Chaos drills that actually help:
- Kill the worker mid-tool
- Return 500 from the model on attempt 1, success on attempt 2
- Make a tool succeed but delay the response past client timeout
- Replay the same workflow ID twice
- Force
needs_reviewand resume after human approve
If these are not in CI or staging playbooks, production will invent them for you.
Product takeaway
Fault recovery is how AI products earn the right to automate. Customers do not need perfect models. They need systems that:
- make partial progress durable
- avoid duplicate side effects
- ask for help when uncertain
- explain what happened when something breaks
That is 0→1 ownership after deployment: not only shipping the happy path, but designing the sad path so feedback and failures become iteration fuel instead of trust debt.