🛡️ Reliability Guide

OpenClaw Error Handling Guide: Retries, Idempotency, and Recovery

•15 min read

Most OpenClaw workflows work on the first try. That is the trap. The demo succeeds, the first week in production succeeds, and then a vendor API returns a 503 at 3 a.m. and your overnight pipeline either dies silently or charges a customer twice. This guide covers how I design error handling for agents that run unattended: retry budgets, idempotency, dead-letter queues, and the judgment calls that decide which failures deserve a second attempt at all.

Nothing here is exotic. Every technique comes from boring distributed systems practice. The agent twist is that your code path now includes a language model making decisions, so the failure surface is wider than a normal script, and some of the worst errors look like successes.

Agents Fail Differently Than Scripts

A cron script fails loudly. Exit code 1, alert fires, you read a log. An agent can fail politely, in fluent English, with a confident summary of work it never did. I have watched an OpenClaw agent report that it reconciled 40 invoices when the API token had expired an hour earlier. The model had seen the tool error, decided the job was impossible, and written the summary a hopeful human would have wanted. No exception was ever thrown.

So the first rule of agent error handling: treat model prose as untrusted until a tool result confirms it. Assertions belong in code, after tool calls, where a bad state produces a real error the runtime can act on.

A Failure Taxonomy Worth Memorizing

Before you write a single retry policy, sort your failures. Every error your workflow can hit falls into one of a few buckets, and each bucket wants a different response.

Transient errors

Rate limits, 502s, timeouts, dropped connections. The request was fine. The world hiccuped. These deserve retries with backoff, and usually nothing else.

Deterministic errors

A 404 on a deleted record, a validation rejection, a malformed argument the tool refused. Retrying these is waste. The same input produces the same refusal forever, and each attempt burns tokens while the model re-explains the refusal to itself.

Semantic errors

The tool succeeded and the result is wrong. The search returned zero rows because the query was bad. The invoice total parsed as a date. These are the dangerous ones, because no status code will save you. You catch them with validation logic and sanity checks, not retry loops.

One more class sits outside the taxonomy: partial completion. The workflow did steps one through four, then died on step five, and steps one through four sent emails, wrote rows, and moved money. That is where idempotency earns its keep. More on it below.

Retry Configuration in openclaw.json

OpenClaw lets you attach retry policy to individual tools and to whole skills. Keep the policy next to the thing it protects, because a sensible budget for a read-only search call is reckless for a payment call.

{
  "tools": {
    "crm.lookup_contact": {
      "retry": {
        "max_attempts": 4,
        "backoff": "exponential",
        "base_delay_ms": 500,
        "max_delay_ms": 8000,
        "jitter": true,
        "retry_on": ["rate_limited", "timeout", "upstream_5xx"]
      }
    },
    "payments.create_charge": {
      "retry": {
        "max_attempts": 1
      },
      "idempotency": {
        "key_template": "{skill_run_id}:{args.customer_id}:{args.amount}"
      }
    }
  }
}

Read that payment block twice. Max attempts of one is not a typo. A charge endpoint gets zero automatic retries from the runtime; if the request times out after the money moved, retrying blindly is how you bill people twice. The idempotency key is the actual protection, and it lets a human-approved replay succeed safely because the API dedupes on the key.

Backoff Math You Can Do in Your Head

Exponential backoff with a 500ms base gives you delays of roughly half a second, one second, two, then four. Four attempts span under eight seconds of waiting. That covers a blip. It does not cover an outage, and it should not try to. If a dependency is down for twenty minutes, the right move is to fail the run, park the work, and let a scheduled job pick it up on the next cycle.

Jitter matters more than people expect. Without it, forty agents that all got rate-limited at 09:00:00 all retry at 09:00:04, and the thundering herd gets rate-limited again together. Randomizing each delay across a window breaks the synchronization. One flag. Turn it on everywhere.

Idempotency: The Whole Ballgame for Side Effects

A retry-safe read is free. A retry-safe write requires that repeating the operation changes nothing beyond the first application. You get that property two ways: the API supports idempotency keys natively (Stripe-style), or you enforce it yourself with a ledger the agent checks before acting.

// Skill-side guard for APIs without native idempotency
async function runOnce(ledgerPath, opId, fn) {
  const ledger = await readJson(ledgerPath, {});
  if (ledger[opId]) {
    return { skipped: true, prior: ledger[opId] };
  }
  const result = await fn();
  ledger[opId] = {
    at: new Date().toISOString(),
    result
  };
  await writeJson(ledgerPath, ledger);
  return { skipped: false, result };
}

The operation ID wants to be deterministic: derived from the run ID plus the business keys of the action, never from a random UUID minted inside the function. Random IDs regenerate on retry and defeat the entire mechanism. I learned that one by sending a client the same weekly report three times in one morning.

Dead-Letter Queues for Failures That Earned Them

Some work should stop trying. A contact whose email bounces twice, a file that fails to parse after a format change upstream, a task the classifier flags as deterministic. Push these onto a dead-letter queue with the full context of the attempt, and review the queue on a cadence. Mine drains every Monday morning, usually in about ten minutes, and the entries it contains have caught two broken vendor integrations before any customer noticed.

{
  "hooks": [{
    "type": "post-execution",
    "event": "skill:failed",
    "when": "attempts_exhausted",
    "action": {
      "type": "enqueue",
      "queue": "dead-letter",
      "payload": ["skill_name", "args", "last_error", "attempt_history"]
    }
  }]
}

Pair this with the instrumentation habits from the observability guide. A dead-letter entry without attempt history is a mystery. An entry with the full error chain is a fix waiting for a cup of coffee.

Circuit Breakers Between Agents

In a multi-agent setup, one agent hammering a sick dependency becomes every agent's problem, because the shared rate limit burns down for the whole fleet. A circuit breaker stops that. After N consecutive failures against a service, the breaker opens and calls fail fast for a cooldown window instead of even attempting the request.

OpenClaw tracks breaker state per tool, shared across sessions, which is exactly the right scope. The orchestration guide covers the coordination side; the error-handling side is a few lines of config and the discipline to leave the cooldown alone when it trips at an inconvenient moment.

Common Failure Modes

Retrying deterministic errors

A 400 response retried five times costs five model round-trips and produces the same refusal. Classify first, retry second.

Retries without idempotency on writes

Timeouts are ambiguous. The request may have landed. Without a dedupe key, the retry is a coin flip against your own data.

Trusting the summary

The agent's final message is prose, not proof. Assert on tool results and side effects before marking a run complete.

Unbounded backoff windows

A retry chain that waits an hour to paper over an outage hides the outage. Fail fast, park the work, requeue it.

A dead-letter queue nobody reads

A queue without a review cadence is a trash can with extra steps. Calendar the drain or do not bother building the queue.

Final Verdict

Error handling for agents is ordinary reliability engineering with one extra hazard: a fluent narrator in the middle of the control flow. Handle the narrator by verifying tool results in code. Handle everything else the way distributed systems people have for decades: classify the failure, retry only the transient ones, make every write idempotent, and give exhausted work a queue and a human.

Start with the payment-grade rule everywhere and relax it where you can prove safety. One attempt, an idempotency key, and a dead-letter queue will carry an unattended OpenClaw deployment through most of what the internet throws at it. When a run does break, the troubleshooting guide is the companion piece for diagnosing it.

⚡

Ready to build?

Get the OpenClaw Starter Kit — config templates, 5 production-ready skills, deployment checklist. Go from zero to running in under an hour.

$14 $6.99

Get the Starter Kit →

Also in the OpenClaw store

🗂️
Executive Assistant Config
Buy
Calendar, email, daily briefings on autopilot.
$6.99
🔍
Business Research Pack
Buy
Competitor tracking and market intelligence.
$5.99
⚡
Content Factory Workflow
Buy
Turn 1 post into 30 pieces of content.
$6.99
📬
Sales Outreach Skills
Buy
Automated lead research and personalized outreach.
$5.99

Get the free OpenClaw quickstart guide

Step-by-step setup. Plain English. No jargon.