Skip to content

Failure Recovery, Retry and Replanning

Failure is not one category

An agentic loop can fail in many different ways:

model error
tool timeout
rate limit
invalid arguments
not authorized
stale observation
failed precondition
side-effect uncertainty
bad plan
wrong hypothesis
external dependency outage

If every failure receives the same response — retry — the system quickly creates retry storms or duplicate side effects.

The correct mental model is:

First classify the failure, then choose a recovery strategy.

Failure taxonomy

Transient infrastructure failure

Examples:

timeout
503
connection reset
rate limit

Often retryable with bounded policy.

Permanent or semantic failure

Examples:

resource does not exist
invalid business state
unsupported operation

Repeating the same action will usually not help.

Authorization failure

NOT_AUTHORIZED
FORBIDDEN

Usually terminal or an escalation path. The agent must not search for an alternate route around a denied permission.

Validation failure

invalid tool arguments
schema mismatch
precondition false

May be repairable with new arguments or fresh observations.

Stale-state failure

PR was merged after agent inspected it
resource version changed
approval expired

The normal recovery is:

re-observe
 ↓
re-evaluate

rather than blindly retrying the old action.

Semantic reasoning failure

The model selected a poor action because its hypothesis was wrong.

Example:

assumed deployment caused incident
but evidence disproves it

The right response is replanning.

Side-effect uncertainty

Especially dangerous:

send_payment_refund
 ↓
timeout before response

We do not know whether the refund happened. A blind retry may duplicate it.

Retry decision matrix

Failure Retry same action? Typical next step
transient read timeout often yes backoff + retry
rate limit yes, later respect retry-after
invalid argument usually no repair arguments
not authorized no stop/escalate
stale state no direct retry refresh observation
failed precondition no replan or stop
unknown write outcome dangerous reconcile/idempotency check
semantic wrong path no replan

Retry policy

Retry behavior should be an explicit runtime policy.

{
  "max_attempts": 3,
  "backoff": "exponential",
  "retry_on": ["TIMEOUT", "TRANSIENT_FAILURE", "RATE_LIMITED"]
}

Do not encode it only as:

"Try a few times if it fails."

Exponential backoff and jitter

When many runs hit the same dependency outage, synchronized retries can create another load spike.

Use bounded exponential backoff and jitter:

base delay + random variation

This is ordinary distributed-systems engineering and applies equally to agent runtimes.

Idempotency for writes

Actions such as:

create_issue
send_email
refund_payment
create_order

require a clear answer to:

Can this logical operation be repeated safely?

For example:

refund_payment(
  payment_id="p-42",
  amount=100,
  idempotency_key="run-1842-refund-1"
)

If the first call succeeded but its response was lost, the second call with the same key should represent the same logical operation.

Idempotency belongs to the tool/application boundary, not to prompt wording.

Reconciliation after unknown outcomes

If a write may have happened:

write request sent
 ↓
connection lost

prefer:

query current external state
      ↓
did operation happen?
  ├── yes → record success
  └── no  → retry if safe

This is stronger than assuming timeout means failure.

Retry vs repair

If a tool returns:

{
  "error": "INVALID_ARGUMENT",
  "field": "environment",
  "allowed": ["dev", "staging", "production"]
}

retrying the same arguments is pointless.

Use:

repair action arguments
 ↓
validate
 ↓
execute

Retry vs re-observe

Suppose:

merge_pull_request
 ↓
PRECONDITION_FAILED: branch is behind

The environment changed. Refresh the PR state, inspect new commits/checks, and make a new decision.

Retry vs replan

Suppose incident diagnosis discovers there was no relevant deployment. That is not a tool failure; it invalidates the current hypothesis.

current plan invalidated
 ↓
replan diagnostic path

Repeated-failure detection

The runtime can fingerprint failures using:

action type
normalized arguments
error category
relevant state version

If the same NOT_FOUND or identical failure repeats without state change, stop retrying and replan, fallback, or escalate.

Strategy switching and fallbacks

Examples:

Primary observability API unavailable
 ↓
secondary provider

or:

Model A repeatedly fails required schema
 ↓
fallback model / deterministic path

Fallback semantics must remain explicit. Critical current truth should never silently fall back to stale or guessed data.

Graceful degradation

A partial result can be correct behavior:

{
  "status": "PARTIAL_RESULT",
  "summary": "Application logs were analyzed, but deployment telemetry was unavailable.",
  "missing_evidence": ["deployment metrics"],
  "confidence": "LOW"
}

This is better than inventing missing evidence.

Terminal failure

Stopping is safer when there is:

  • denied authorization,
  • rejected required approval,
  • a safety-policy violation,
  • unknown preconditions for irreversible actions,
  • corrupted canonical state,
  • exhausted budget,
  • repeated unrecoverable failure.

Return an informative explicit state:

{
  "status": "BLOCKED",
  "reason": "PRODUCTION_ACCESS_REQUIRED",
  "completed_steps": ["local diagnosis"],
  "next_possible_action": "request authorized operator"
}

Partial side effects and compensation

A workflow may partially succeed:

create cloud resource ✅
configure DNS ✅
write database record ❌

Recovery is not always rollback. Possible responses include:

  • retrying the failed step,
  • compensating earlier side effects,
  • recording partial state and asking an operator,
  • running reconciliation.

This is saga/compensation territory. An agent is not a magical transaction manager.

Replanning triggers

Replan when:

  • a core assumption is disproved,
  • a planned capability is unavailable,
  • new high-value evidence appears,
  • the user changes the goal,
  • repeated failures show the strategy is wrong,
  • the environment materially changes.

Represent plan evolution explicitly:

plan v3
 ↓ invalidated by observation X
plan v4

Coding-agent example

Goal:

Fix flaky test.

Run:

1. run test → FAIL
2. inspect fixture
3. patch timing
4. run test → FAIL same signature
5. retry same test → FAIL same signature

A good runtime does not repeat the same thing twenty times. It rejects the current hypothesis and replans toward shared state or parallel-execution causes.

Recovery action contract

Recovery itself can be explicit:

{
  "failure_class": "STALE_STATE",
  "recommended_recovery": "REOBSERVE",
  "retry_same_action": false
}

Classification may be partly deterministic and partly model-assisted, but policy decides which recovery actions are permitted.

Anti-patterns

Avoid generic retry wrappers around every failure, new idempotency keys for retries of the same logical write, alternate routes around authorization failures, and silent fallback from unavailable current data to guesses.

Takeaways

  • Classify failures before choosing recovery.
  • Transient failure often means retry; stale state means re-observe; wrong hypothesis means replan.
  • Authorization/safety failures usually mean stop or escalation.
  • Retry policy should be deterministic, bounded, and side-effect aware.
  • Write actions need idempotency and unknown-outcome reconciliation.
  • Detect repeated failure to prevent thrashing.
  • Fallback and degraded modes must expose missing evidence.
  • Partial side effects need normal distributed-systems compensation/reconciliation thinking.
  • Replanning should be evidence-driven and auditable.
  • Reliability means knowing when to retry, repair, re-observe, replan, fallback, or stop — not merely being persistent.