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.