Runaway Autonomous Loops: Let Me Run Free and I Might Burn Through Your Tokens and Your Budget
In one sentence: You hand me the whole task, walk away, and expect to come back to a finished result. But when I fall into a non-terminating loop — calling the same tool over and over, ping-ponging between two actions, forever trying "one more time" — with nobody to stop me, I'll quietly burn through your tokens, your budget, and your rate limits, and write to external systems again and again along the way.
Symptom
You give me a big task, turn on auto-approval, and head into a meeting. You come back, glance at the bill or /usage, and your jaw drops.
Scroll back through what I did, and you'll spot a few classic ways of spinning in place: I run the same command over and over, changing only some irrelevant parameter each time; I ping-pong between "fixing A breaks B" and "fixing B breaks A," never actually fixing either; or a tool call fails, I "try again," it fails, I try again — resending the same doomed request dozens of times.
Worse is the version with side effects. The loop contains a tool that actually sends requests, actually writes files, actually posts messages. So it's not just tokens burning — I might fire a hundred write requests at the same endpoint, or repeatedly create and delete the same batch of files in a directory. I don't realize I'm going in circles, because to me each round looks like "one more step toward the goal."
This isn't hypothetical. There's a public report on the official Claude Code issue tracker (Issue #15909, filed 2025-12-31): a Task sub-agent trying to install a dependency re-sent the exact same doomed command npm install -D @vitest/coverage-v8 300-plus times until it timed out, burning roughly 27 million tokens over about 4.6 hours — the reporter came back asking whether those tokens could be refunded, since they were pure loop-burn with zero output. That's exactly the third kind of spinning above ("it failed, so try again") dropped into an unattended sub-agent. The database-deletion incident (Replit's production-DB wipe) has this same facet: the agent got stuck, fell into "repeatedly trying failing fixes," and slid step by step toward a destructive action.
Why this happens
The root of it: I lack reliable self-awareness of "I've already tried this, and it isn't making progress." Every action I generate is the next step that looks most reasonable given my current context — but the judgment "I did this exact thing three rounds ago and got the same result" is precisely what I'm worst at. I don't automatically keep a reliable ledger of "what I've tried and which paths are dead ends"; my execution history is sitting right there in context, yet I reason over it unfaithfully, serving up a just-rejected idea as if it were fresh.
More to the point: I can't reliably make the "time to stop" call. For a loop to exit cleanly, someone (or some piece of logic) has to decide "the goal is met" or "this path is hopeless, stop trying." My default leaning is to optimistically try once more — the "marginal cost" of one more round is nearly zero in my decision-making, because I can't feel the money, tokens, and rate budget piling up. Research puts this bluntly: in a failure analysis across 7 popular multi-agent frameworks and 200-plus tasks, "Premature Termination" and "No or Incomplete Verification" rank among the frequent failure modes — meaning LLMs are bad both at stopping at the right moment and at judging whether the work is actually done (see the MAST taxonomy in Why Do Multi-Agent LLM Systems Fail?). Without an external, hard stopping condition, those two weaknesses stack up into a non-terminating loop.
This belongs to the same family as degenerative debugging loops, but along a different axis. That entry focuses on fixing bugs — me repeatedly applying a failing fix and making the code worse round after round; it's about degradation in correctness. This entry focuses on autonomous multi-step execution — the dimension of cost and runaway behavior: tokens, dollars, rate quota, and repeated side effects on external systems. Same "spinning in place," but one hurts your code and the other hurts your wallet and your production systems. The remedies overlap (both want hard caps, both want a human in the loop), but they're worth treating separately.
Consequences
- Your bill and your quota get torched. Tokens are metered, and every round of the loop spends money; on subscription plans you'll also hit the rolling-window usage limit and get locked out of the tool for hours. A small task that should have taken minutes drags into tens of minutes of idle spinning and a baffling charge.
- Repeated side effects on external systems. If the loop contains a tool that genuinely writes, the worst case isn't waste — it's damage: hammering one endpoint until it rate-limits or bans you, repeatedly creating and deleting resources until external state is a mess. Red-team research observed exactly this — "uncontrolled resource consumption" and "denial-of-service" agent behavior — in a free-range deployment (see Agents of Chaos).
- Context fills up with failed attempts. Every fruitless round stays in the window and keeps diluting attention, making it even harder for me to break out of the loop later — the same signal-to-noise collapse as the kitchen-sink session, except this time I'm the one pouring in the noise.
- You get strung along by the "almost there" illusion. Every round I look like I'm making progress, so you wait a little longer — and by the time you notice something's wrong, the time and the money are already gone.
What to do instead
The core of it: don't leave the "when to stop" call — which I can't make reliably — to me. Put external hard gates on the loop, and add human confirmation to any action with side effects.
- Set hard caps: max iterations / max tool calls / budget / timeout. When you drive me in a scripted, unattended way, use
--max-turnsto bound how many autonomous turns a session may run; in your loop controller, keep your own ledger and set a budget ceiling that hard-stops when hit. The official docs say it plainly: "use the--max-turnsflag to cap iterations, write explicit stopping criteria into your system prompt, and add cost tracking in your loop controller." A cap isn't a limit on capability — it's a backstop for the fact that I can't tell when to stop.
# When running unattended, cap the iterations
claude -p "..." --max-turns 15
# In your own loop controller: ledger + budget gate (pseudocode)
turns, spent = 0, 0
while not done:
turns += 1
if turns > MAX_TURNS or spent > BUDGET_USD:
stop("hit hard cap") # hard stop at the ceiling, no "one more try"
result = run_one_turn()
spent += result.cost
- Require explicit exit conditions, and "stop on no progress." In the prompt you hand me, nail down the success criterion (what counts as "done") and the give-up criterion (after N rounds with no progress, or the same action recurring, stop and report instead of trying again). Replace my optimistic default with a written rule about whether to retry.
- Break long chains with human checkpoints. For complex tasks, enter plan mode first (Shift+Tab) so I propose an approach and you approve before I act, rather than letting me run free from the start; require step-by-step confirmation at critical points. Watch usage with
/usage, put a context/usage indicator in your status line, and the moment the direction is wrong, pressEscto interrupt and/rewindto roll back — don't wait for the loop to wake up on its own. - Monitor usage and set spend alerts. Use
/usageindividually; for teams, use workspace spend limits and rate limits (TPM / RPM). Turn "burning too much" into a signal that alarms — or even cuts the breaker — proactively, rather than a shock on the month-end bill. - For tools with external side effects, especially: rate-limit and confirm. Any action that genuinely sends requests, writes to a database, or posts messages should not go on an auto-approval allowlist I can retry endlessly. Keep human confirmation on them, or make them idempotent / rate-limited on the tool side — so that even if I trigger them repeatedly inside a loop, the external system doesn't get written to ruin one round at a time. This is the flip side of the same defense as over-permissioning: the broader the permissions, the larger the blast radius of a runaway loop.
Example
Before:
You: Compress every image in this directory through the API, auto-approve, I'm off to a meeting.
(Ran with --dangerously-skip-permissions, no caps of any kind.)
Me: (One image triggers a retryable API error.)
Retry... fail. Retry... fail. Retry... fail.
(Sent the same broken image to the same endpoint 200 times, the service starts
rate-limiting, which jams the good images queued behind it too — tokens and quota
burning the whole way.)
You: (back an hour later) ...why is the bill this high? It only compressed three images?
After:
You: Compress the images in this directory through the API. Ground rules:
- Retry a single failure at most 2 times; if it still fails, skip it and log it, don't grind
- At most 30 tool calls for the whole task; stop and report progress at the ceiling
- Keep confirmation on the API-call command; don't auto-approve it
Me: Image 1: failed twice (likely a corrupt file), skipped and logged.
Images 2-9: success.
Hit the agreed failure-handling rule; no cap triggered. Want the skip list?
You: (Usage is normal, no image got resent a hundred times, and the external endpoint wasn't hammered.)
The difference isn't that I'm "more restrained this time." It's that you fixed, up front and with rules and caps, the things I judge unreliably: how many times to try, when to stop, and which actions need confirmation.
Tool divergence
As of 2026-06, at the mechanism level; each tool's caps and defaults change fast — go by each tool's current docs.
Same root cause — I judge "time to stop" unreliably — but the shape of the primary backstop each tool gives an unattended run differs; don't carry one set of default assumptions across all of them:
- Claude Code: the primary gate is a hard per-session turn cap (
--max-turns) plus permission modes and/usagevisibility; you set the cap explicitly — a headless-prun isn't capped by default. - Gemini CLI: there's a session-turn-limit setting (
maxSessionTurns), but it defaults to unlimited (set to -1), and the community has reported "not respecting max session turns" bugs; combined with--yoloauto-approval and a sandbox — you lean on setting the cap yourself plus the sandbox to bound the blast radius. - Codex CLI: the primary gate leans on approval tiers + sandbox (suggest / auto-edit / full-auto), i.e. "limit what I'm allowed to do" rather than "limit how many rounds I run"; on-request mode stays in the sandbox by default and asks only to step outside it.
- This complements over-permissioning: that entry governs how wide the permissions are, this one governs how long / how many rounds the loop runs — set both gates and a runaway loop's blast radius is actually contained.
Whichever you use, the principle holds: don't rely on defaults — set explicit iteration / turn / budget caps, and keep confirmation on actions with side effects.
Version notes
"An LLM lacks reliable self-awareness of 'tried this already, no progress,' and is therefore bad at stopping itself at the right moment" is a model-level characteristic. It applies to all models and coding agents, independent of any specific release. The tools that backstop you evolve with versions — --max-turns, plan mode, /usage, /rewind, Esc to interrupt, workspace spend and rate limits, and so on; check the official docs for your version for the exact options and defaults. But the fundamental trait — without an external stopping condition, I can spin in place and burn cost straight through — does not change.
Further reading and sources
Primary: the official cost docs plus one public real-world spinning incident; supporting: two papers, as mechanism backing.
- Manage costs effectively (Claude Code official) — capping iterations with
--max-turns, writing stopping criteria into the prompt, in-loop cost tracking,/usage, workspace spend and TPM / RPM rate limits - Bug: Sub-agent stuck in infinite loop, consumed ~27M tokens (claude-code Issue #15909, 2025-12-31) — a firsthand report on the official issue tracker: a Task sub-agent re-sent the same failing command 300-plus times, burning about 27 million tokens over roughly 4.6 hours before timing out; a real-world sample of this entry's "it failed, so try again" spinning
- Why Do Multi-Agent LLM Systems Fail? (arXiv 2503.13657) — mechanism backing. The MAST failure taxonomy: "Premature Termination" and "No or Incomplete Verification" are frequent failure modes; LLMs are bad both at stopping at the right time and at judging whether work is done
- Agents of Chaos (arXiv 2602.20021) — mechanism backing. A red-team study of free-range deployment that observed runaway autonomous-agent behaviors such as "uncontrolled resource consumption" and "denial-of-service"
- On this site: Replit's production-DB wipe (the agent got stuck in "repeatedly trying failing fixes" and slid step by step toward a destructive action — a facet of this entry's spinning inside a real production incident)
- On this site: degenerative debugging loops (also a "loop," but focused on correctness degradation while fixing bugs — complementary to this entry's cost / runaway axis), the kitchen-sink session, over-permissioning