AI agent retry storms and infinite loops — runaway retries that burn tokens and budget
38 patterns
AI agent retry storms and infinite loops — runaway retries that burn tokens and budget. We've tracked 38 distinct AI Agent Retry Storms patterns across production AI agent deployments — most commonly in Operations, Support Services, Customer Service systems. One recurring example: Approval Delegation Loop — An approver who is unavailable delegates their approval authority to another approver, who in turn delegates back to the original approver (or to a third party who delegates furthe…
Each pattern below documents the specific failure mode, why it happens, and how teams have mitigated it in practice — use this page as a reference when building guardrails, writing evals, or debugging an agent that's already misbehaving in this way.
An approver who is unavailable delegates their approval authority to another approver, who in turn delegates back to the original approver (or to a third party who delegates further, forming a longer cycle). The delegation graph has no cycle detection, so the request bounces indefinitely between the delegated parties, or the workflow engine detects the loop only after it has already re-notified the same approvers dozens of times.
Employees Flagged as High Attrition-Risk by the Retention-Prediction Model Are Systematically Deprioritized for Growth Opportunities, Stretch Assignments, and Promotion Consideration by Managers Aware of the Score, Causing the Flagged Employees to Actually Leave at Higher Rates as a Consequence of the Flag Itself
Many APIs specify an expected retry envelope for failed requests — a minimum delay before retrying (to avoid hammering a recovering service) and a maximum delay (beyond which the server considers the client "gone" and drops queued state, such as an idempotency reservation or a rate-limit grace window). An agent's retry logic, especially generic exponential-backoff code reused across many tools, frequently ignores tool-specific envelope hints (a `Retry-After` header, a documented min/max, or a jittered range) and either retries too fast — getting throttled harder or banned — or waits too long — missing a narrow retry window and losing queued work or an idempotency token.
An agentic system experiences a small, low-severity failure in one component — a single slow dependency, a handful of dropped requests, a brief queue backup — and instead of staying proportionate, the failure grows in magnitude as it propagates through downstream components. Each hop adds retries, duplicate work, or overcorrection, so a fault that started as a 2% error rate in one service arrives at the edge of the system as a full outage. The agent-specific twist is that autonomous retry loops and multi-step planning amplify faster than in traditional systems, because an agent facing a transient tool failure often retries the entire multi-step plan rather than the single failed call.
After a cascading failure hits several components, each component recovers independently — resuming from its own local checkpoint, cache, or retry queue — without coordinating with the others on what the "true" post-incident state should be. The result is that components which were consistent before the cascade come back online in mutually inconsistent or conflicting states: one service thinks an order is confirmed, another thinks it was cancelled, and a third has no record of it at all. This is distinct from a single component failing to recover correctly; the problem is specifically that multiple components recover to different, incompatible versions of the truth.
The very mechanisms deployed to make a system resilient — retries, circuit breakers, health-check-triggered auto-restarts, failover — become active contributors to a cascading failure instead of containing it. A retry policy that seemed reasonable in isolation adds load to an already-struggling dependency; a circuit breaker's half-open probe traffic re-triggers the failure it just tripped on; an auto-restart policy cycles a struggling instance in a tight loop that never lets it recover. This pattern is specifically about resilience infrastructure making things worse, distinct from cascade-amplification (which is about magnitude growth generally) and cascade-timeout-interaction (which is about timeout settings specifically).
Different layers in a call chain are configured with timeout values that were each set reasonably in isolation but interact badly when combined, actively amplifying a cascade instead of bounding it. The classic bad pattern is an upstream timeout that is shorter than or too close to a downstream timeout, so the caller gives up and retries while the original call is still in flight and still consuming resources on the downstream system — doubling load without reducing it. This is a distinct, specific mechanism from general cascade-amplification: the trigger here is specifically the numeric relationship between timeout values at different layers, not retry policy or resilience-mechanism design broadly.
Self-Service Deflection Agent Routes a Customer Through a Closed Loop of FAQ Articles That Each Point Back to One Another, Never Reaching a Resolution or a Human Handoff
The agent keeps asking clarifying questions turn after turn without ever committing to an interpretation and proceeding, even after the user has provided enough information to act, or has explicitly said to just make a decision. Each answer the user gives triggers a further question rather than progress, and the conversation never converges on output. This differs from clarification-irrelevant (a wrong single question) in that the loop never terminates at all — the failure is in the stopping condition, not the question content.
A sudden spike in concurrent inference requests — from a traffic burst, a retry storm, or an agent fan-out pattern that issues many parallel sub-calls — exhausts GPU memory, CPU, or connection-pool capacity faster than autoscaling or admission control can react. Instead of degrading gracefully, the serving layer either crashes (taking down in-flight requests and forcing expensive retries) or silently over-admits requests into an already-saturated batch, driving per-request latency and cost up simultaneously as the system thrashes rather than serves.
A tool call's underlying TCP/TLS connection times out — a transient blip caused by network jitter, a brief DNS hiccup, or a momentary vendor load spike — and the agent has no retry logic wrapping the call. Instead of treating the timeout as a one-off, recoverable event, the agent surfaces it as a hard tool failure: it aborts the current task, marks the tool "unavailable," or hands the user a generic error, even though a second attempt a moment later would very likely have succeeded.
A tool vendor changes what an existing error code means — repurposing a generic `400 Bad Request` to also signal a new condition like "rate limited by a downstream partner" or reusing `409 Conflict` for a newly introduced idempotency-key collision case — without incrementing the API version or announcing a breaking change. The agent's error-handling logic, written against the old, narrower meaning of that code, applies the wrong recovery strategy (e.g. retrying a request that will never succeed, or treating a transient condition as permanent), and because the HTTP status and error code string are unchanged, nothing about the failure looks abnormal at the transport level.
A handed-off task gets executed more than once because the handoff mechanism retries on a suspected failure (timeout, dropped acknowledgment, transient network error) without any way to detect that the receiving agent already processed the original attempt. The receiving agent has no concept of "I've seen this task ID before" and treats each retry as a fresh, independent instruction, resulting in duplicate side effects — a second email sent, a second charge issued, a second record created.
Critic and reviser agents -- a common pattern in LangGraph reflection loops -- keep critiquing and revising each other's work endlessly, with no termination condition to stop once the debate stops making progress.
A single logical operation an agent performs fans out into calls against several independent downstream integrations, each with its own separately-published rate limit, and the agent's throughput planning accounts for at most one of them (usually the loudest or most recently hit limit) rather than the effective ceiling set by whichever integration is most constrained. Because none of the integrated systems know about each other's limits, and the agent's own rate-limiting logic is typically tuned per-integration rather than per-workflow, a request pattern that looks safe against every individual system's published limit can still produce compounding overload: a retry triggered by one system's 429 fans back out and re-hits every other system in the same operation, multiplying load precisely on the systems that weren't the original bottleneck.
An agent calls an external integration with a timeout shorter than the operation actually needs to complete on the far side, gives up and treats the call as failed, and then acts on that failure assumption — retrying, falling back, or notifying a user that the action didn't happen — while the original call is, in fact, still running to completion on the downstream system. When the downstream operation succeeds after the agent has already moved on, the agent's decision was made on stale information: it may issue a duplicate request (double-charging a payment, double-booking a resource), or it may leave the user with an incorrect "this failed" outcome for an action that actually succeeded. This is a correctness/state-consistency failure specific to one integration point, distinct from the broader dynamic where mismatched timeouts across a call chain amplify load system-wide.
A tool's documented latency SLA (e.g., "p99 under 500ms") is regularly exceeded in actual production traffic, and the agent's own timeout and retry logic — tuned to trust that documented figure — fires prematurely relative to the tool's real behavior, or conversely the agent's own downstream SLA commitment to its users gets breached because it inherited an unrealistic latency assumption from the upstream tool. Either way, the mismatch between advertised and actual latency propagates as a reliability problem that looks like the agent's own bug.
An agent's generated output — a chat completion, a streamed response, or a payload returned from a tool call — gets cut off mid-generation or mid-transmission (hitting a `max_tokens` cap, a proxy timeout, a streaming connection drop, or an intermediate buffer limit), and nothing in the pipeline detects or flags that the content is incomplete. The truncated fragment is syntactically plausible enough (a sentence that just stops, or JSON that's missing its closing braces) that it gets parsed, stored, or displayed as if it were the complete, intended output, rather than triggering a retry or an explicit incompleteness signal.
A tool vendor prices calls at a low baseline rate up to a sustained throughput threshold (e.g. 10 requests/second) but charges a significant premium — sometimes 5-10x — for requests above that threshold within a billing window. An agent's retry-with-backoff logic, built purely to handle rate-limit errors or transient failures, resubmits requests in tight clusters after a failure or during a burst of user activity, pushing throughput over the baseline and triggering premium billing that the agent's cost model never anticipated because it only models the advertised baseline rate.
A tool enforces a per-minute rate limit, and the agent hits it during a tight retry loop: an initial call fails or is slow, the agent retries immediately without backoff, and each retry itself consumes another slot against the same per-minute budget — compounding the original problem instead of resolving it. What starts as one transient failure turns into a cascade of rate-limit rejections that persists well past whatever caused the first failure.
Some tools enforce rate limits with zero grace period: the moment a request is rejected with a 429, the very next request — even one sent a fraction of a second later, even one that would normally be well within budget — is also immediately rejected, with no brief cooldown signal or soft-warning phase before the hard cutoff. Agents that respond to the first 429 by retrying quickly (assuming a brief backoff is enough) get rejected again immediately, and if their backoff strategy isn't tuned for a limit with no forgiveness, this produces a tight loop of rapid-fire failures instead of a clean recovery.
A tool returns standard or vendor-specific rate-limit headers on every response (e.g., `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `Retry-After`) that would let the agent pace itself proactively and avoid ever hitting a hard rejection — but the agent's HTTP client or tool wrapper doesn't parse or act on them. The agent keeps calling at its own fixed cadence until it eventually gets rejected outright, throwing away information the vendor was actively handing it for free.
Agent Resolves Each Support Contact From a Customer in Isolation, Failing to Recognize That the Same Underlying Issue Has Been "Resolved" Multiple Times Without Actually Being Fixed
A tool's actual uptime falls short of its advertised availability SLA (e.g., "99.9% uptime" translating to roughly 43 minutes of allowed downtime per month, but real outages exceeding that budget). The agent, built with no fallback path because the SLA implied outages would be rare and brief, treats every outage as an unexpected, unhandled condition — retrying blindly, failing the entire user-facing workflow, or queuing work indefinitely — rather than having a designed response for a scenario the SLA math said should barely ever happen but that occurs often enough in practice to matter.
An agent retries a tool call after a timeout, an ambiguous error, or a crash-and-resume, assuming that calling it again is safe because "retrying is always safe." Some tools are not idempotent — calling them twice with the same arguments produces two distinct side effects (two charges, two emails, two created records) rather than converging to the same end state. The agent's retry logic doesn't distinguish between tools where a duplicate call is harmless and tools where it corrupts state or produces a real-world duplicate action.
Some tools track retry attempts server-side per operation (keyed by an idempotency key, request ID, or resource ID) and permanently block further retries once a maximum attempt count is reached within a window — for example, a payment gateway that hard-fails an idempotency key after 5 attempts, refusing all further retries regardless of the reason for prior failures. An agent's own retry counter, especially one held in process memory or reset on deploy/restart, frequently loses sync with this server-side count, so the agent believes it has budget for more attempts and keeps retrying into a wall that will never open, wasting time and obscuring the real failure behind a misleading "max retries exceeded" or generic error each time.
A traffic-routing configuration meant to apply uniformly during a version rollout — a canary weight, a header-based version pin, a geographic or tier-based split — instead applies inconsistently across different request paths, entry points, or protocols. A user hitting the agent through the REST API gets routed according to the intended canary percentage, while the same logical traffic arriving through a WebSocket streaming endpoint, an internal service-to-service call, or a retry path bypasses the routing rule entirely and always lands on one version regardless of the configured split. The result is that "5% canary" is only true for some fraction of actual traffic, while another slice is either entirely exposed to the new version or entirely shielded from it, undermining both the safety intent and the statistical validity of the rollout.
A tool's webhook delivery fails repeatedly against the agent's receiving endpoint — due to a transient outage, a misconfigured URL, or a deploy-time gap — and the vendor gives up retrying after a fixed number of attempts or a fixed time window. Once that budget is exhausted, the event is dropped permanently with no further attempt and, in many implementations, no notification to the receiver that delivery ultimately failed. The agent never learns the underlying event happened at all, and nothing in its own logs points to the gap since the failure occurred entirely on the vendor's side.