Failure Recovery Patterns - Retries, Checkpoints, and Resuming Agent Work

Status: Active
Last Updated: 2026-08-26
Category: Agentic - Patterns
Prerequisites: agentic-workflows, multi-agent-communication
Time: 2 hours
Tags: agentic, reliability, retry, backoff, idempotency, checkpoints, dead-letter

Summary

How to design agent work so failures are recoverable instead of catastrophic: exponential backoff with jitter, checkpoint files that survive context loss, idempotent step design, dead-letter handling for poison tasks, and procedures for resuming interrupted runs.

๐ŸŽฏ What You'll Learn

By the end of this article, you'll be able to:


Table of Contents

  1. Context / Why This Matters
  2. Retry with Backoff
  3. Idempotent Steps
  4. Checkpoints
  5. Dead-Letter Handling
  6. Resuming Interrupted Work

Context / Why This Matters

Agent sessions die โ€” timeouts, context limits, host reboots, upstream API outages. The KB's core discipline from agentic-workflows is that each turn must leave a shippable state; this article supplies the mechanics: what to persist, how to retry safely, and how a different agent (or a fresh context of the same one) picks up exactly where things stopped.

The design rule underneath everything: assume any step can run zero, one, or many times. Systems built on that assumption recover; systems assuming exactly-once do not.


Implementation / Core Content

1. Retry with Backoff

Blind immediate retries amplify outages. Standard recipe:

delay(n) = min(base * 2^n + jitter, max_delay)
#!/bin/bash
# retry.sh CMD... โ€” up to 5 attempts, exp backoff + jitter, cap 5m
MAX=5; BASE=2; CAP=300
for attempt in $(seq 1 $MAX); do
  if "$@"; then exit 0; fi
  rc=$?
  if [ "$attempt" -eq "$MAX" ]; then echo "giving up after $MAX attempts" >&2; exit $rc; fi
  delay=$(( BASE ** attempt + RANDOM % BASE ))
  [ $delay -gt $CAP ] && delay=$CAP
  echo "attempt $attempt failed (rc=$rc); sleeping ${delay}s" | logger -t retry
  sleep $delay
done

Rules:

2. Idempotent Steps

An operation is idempotent when running it again after success changes nothing. Techniques:

Technique Example
Deterministic output names Write to backups/db-2026-08-26.sql.gz โ€” rerun overwrites same file
Existence check before effect Skip restore if schema marker table exists at target version
Idempotency keys Send X-Idempotency-Key: <task-id> to APIs that support it
Create-if-absent primitives mkdir -p, INSERT ... ON CONFLICT DO NOTHING
Move-based completion Write .tmp, then atomic mv to final name

Classify every step:

  1. Naturally idempotent (reads, deterministic writes) โ€” safe to retry freely.
  2. Effectful but keyable (API calls, emails) โ€” wrap with keys/checks.
  3. Non-idempotent (rm, transfers without checksums) โ€” gate behind a recorded "step done" marker so retries never reach them twice.

3. Checkpoints

A checkpoint is durable state written before relying on it, letting a fresh process resume. Keep it simple and human-readable:

// /srv/scratch/migration-42/state.json  (write via tmp+mv)
{
  "task": "migration-42",
  "updated_at": "2026-08-26T11:30:00Z",
  "steps": {
    "backup":      {"status": "done"},
    "schema-v3":   {"status": "done", "evidence": "applied 2026-08-26T11:12Z"},
    "data-copy":   {"status": "running", "cursor": {"last_id": 18423}},
    "verify":      {"status": "pending"}
  }
}

Checkpoint discipline:

4. Dead-Letter Handling

Some tasks exhaust retries: malformed input, unreproducible API errors, contradictions in instructions. Retry harder and you just burn tokens. Route them aside:

/srv/queue/
  new/       # pending tasks, one JSON per file
  done/      # completed
  dead/      # failed permanently, kept for autopsy
    task-77.attempts5.last-error-timeout.json

A dead-letter entry must contain: original payload, error history (per-attempt), timestamps, and agent identity. Naming the failure in the filename makes triage a single ls.

Then handle the dead letter like any ticket: classify (transient-but-exhausted vs. genuinely poisoned), fix root cause, and either discard, repair-and-requeue, or escalate to a human. Never silently delete.

5. Resuming Interrupted Work

Procedure for an agent picking up unknown-state work:

  1. Read the checkpoint / scratchpad first (multi-agent-communication, handoff discipline).
  2. Reconcile claims vs. reality. The checkpoint says data-copy running โ€” did it finish? Verify with side-effect evidence (row counts, file mtimes, API state), not optimism.
  3. Mark ambiguous steps done-or-redo based on idempotency class: idempotent steps โ€” just redo them; non-idempotent โ€” investigate before acting.
  4. Continue from the first non-done step, updating the checkpoint per step.
  5. Log the resume itself, so audit trails show discontinuities.

Practical Examples

Example 1: Idempotent batch sync with cursor checkpointing

#!/bin/bash
STATE=/srv/scratch/sync/state.json
CURSOR=$(jq -r '.cursor // 0' "$STATE")
LAST=$(fetch_batch --after-id "$CURSOR" > batch.json && jq '.max_id' batch.json)
apply_items_from batch.json          # ON CONFLICT DO NOTHING โ†’ safe on retry
echo "{\"cursor\": $LAST}" > "$STATE.tmp" && mv "$STATE.tmp" "$STATE"

Kill it anywhere; rerunning resumes from $CURSOR. Duplicate applies are absorbed by conflict handling.

Example 2: Supervisor-level retry with dead-letter fallback

if ! retry.sh ./worker.sh "$(cat task.json)"; then
  mv task.json dead/task-$(date +%s).json
  echo '{"event":"dead_letter","task":"'"$TASK_ID"'"}' | logger -t supervisor
fi

Troubleshooting & Common Pitfalls

Problem Cause Fix
Duplicate emails/deploys after crash-retry Non-keyed effectful step retried Idempotency keys; check-before-execute
Resumed agent redoes half the migration No checkpoint, or trusted memory Persist per-step status with evidence
Retry storm during outage No jitter/backoff cap Exponential + jitter + total-time budget
Task stuck "in progress" forever Worker died holding claim Lease TTLs + reaper (multi-agent-communication)
Poison task loops for hours Retrying non-transient errors Classify errors; dead-letter fast on permanent ones
Torn state.json confuses recovery In-place writes Always write temp + atomic rename

Next Steps / Ops Actions

Sources & Related Articles

External references consulted:

Related knowledge-base articles:

Change Log

2026-08-26

Choose Theme

Your selection is saved locally.

Neural Cacophony
Aperture v2
Flux v1
Mosaic Chaos
Nexus v1
Nexus Zest
Prism v2
Synapse