A human working on a task develops an instinct for when it's taking too long and stops to reassess. An autonomous agent has no equivalent instinct unless one is built in — it will keep calling tools, keep re-reading files, keep retrying a failing approach with minor variations, for as long as the harness lets it, because nothing in a language model's loss function corresponds to "this is getting expensive." The failure isn't a single catastrophic bug; it's usually a fix-verify-fail cycle, a subagent spawned inside another subagent's retry, or a large file re-read on every iteration of a loop that should have cached it once. Each iteration looks locally reasonable. The sum does not.
A monthly spend alert tells you the loop happened after it already ran to completion, possibly hundreds of times, across every task that shared the same bug. A per-task budget — a token ceiling or a dollar ceiling checked before each model call, not after — stops the same loop on its first occurrence, while there's still a live process to abort and a stack trace showing what it was doing. The two aren't substitutes for each other: the account-level alert catches the failure mode you didn't anticipate, and the per-task budget catches the ones you did. Systems that only have the first only ever notice cost problems in arrears.
A legitimately hard task and a stuck loop can have identical early token curves — both start slow and keep consuming budget. The signal that separates them isn't total spend, it's marginal progress per unit spend: is each additional tool call narrowing the problem (new information, a passing subtest, a shrinking diff) or repeating a shape already seen (the same file re-read, the same command re-run with the same result, the same error re-triggered after a "fix")? A budget policy that only checks a hard ceiling will let a stuck loop run all the way to the ceiling before stopping. A policy that also checks for repeated state — same tool, same arguments, same result, more than once or twice — catches the stuck case far earlier than the hard case, without needing to reason about whether the task is "supposed" to be expensive.
| Signal | What it means | Response |
|---|---|---|
| token spend rising, distinct new state each step | hard but progressing | let it continue, within the outer ceiling |
| same tool + arguments repeated with the same result | stuck in a loop, not making progress | abort well before the ceiling — more spend won't fix repetition |
| subagent spawns subagent spawns subagent | unbounded fan-out, cost multiplies per level | cap spawn depth explicitly; don't rely on each level's own budget alone |
| single call far larger than the task's typical calls | likely a full-file re-read or an unfiltered dump that should have been scoped | flag for review even if under budget — it's a sign the approach, not just the spend, is off |
When a task is approaching its budget, the cheapest lever usually isn't stopping — it's finishing the remaining steps on a smaller, cheaper model, especially for mechanical work like re-running a test suite or formatting a diff that doesn't need the reasoning tier that started the task. This only works if the budget system tracks spend per task rather than per model call in isolation, so it can see the remaining budget and choose a tier that fits it, instead of finding out only at the final call that there wasn't enough left to finish properly. Reserve a hard abort for when even the cheap tier can't complete the remaining work inside what's left — that's a real signal that the task needs to be re-scoped, not just re-run.
The mistake to watch for: a budget check written once, against the pricing and context limits of whichever model was current at the time, then never revisited. Token costs and context windows change fast enough that a hardcoded ceiling either goes stale as a real constraint (too low for a newer, cheaper model) or stops being a meaningful check at all (too high to catch anything before it's already expensive). Tie the ceiling to the current model's actual price and limits, not a number picked once and forgotten.
A budget stops a loop from running too long. It doesn't decide how much context that loop should be carrying while it runs — see context window management for long-running agents.
A budget caps total spend. It doesn't slow down the retry rate against a single struggling dependency — see rate limits and backoff for agent tool calls for that half of runaway-loop prevention.
A budget reacts to spend that's already happened. Caching prevents some of that spend from happening at all, by skipping calls whose answer is still valid — see caching tool calls for agents for how to do that without serving a stale result.