BlogAgent Loops That Burn Money While You SleepHit · Load & Latency

Agent Loops That Burn Money While You Sleep

SR
Sofia Reyes · March 2026 · 11 min read

TL;DR

A stuck agent does not crash, it keeps calling tools and re-sending its own swelling context at full price, all night, while every dashboard stays green. In November 2025 a four-agent research pipeline did exactly this: two agents ping-ponged requests for 264 hours and ran up a $47,000 bill before the billing console surfaced a number big enough to stop it. The cost driver is structural, each loop iteration re-pays for the whole transcript, so step 50 can cost 30x step 1. Request limits and timeouts do not catch it. The fix is a hard per-run token and iteration budget, enforced in three independent places, plus a deliberate test that drives an agent into a loop and proves the guardrails fire.

A crash stops. A loop does not.

The thing about a crash is that it has the decency to stop. Stack trace, exit code, pager, done. You can sleep through a crash because the system falls over and waits for you.

An agent loop has no such decency. It does not crash, it keeps going. Every iteration it picks a tool, calls it, gets a result it cannot quite use, and reasons its way into trying again, while the context window swells and every turn re-sends the entire transcript to the model at full price. The dashboards stay green because nothing is broken: latency is fine, the error rate is zero, the agent is, by every signal your ops team watches, working perfectly. It is just working perfectly at several hundred dollars an hour, and it started at 11pm.

This is not a thought experiment. A widely-circulated November 2025 post-mortem describes a market-research pipeline of four LangChain agents in which an Analyzer and a Verifier began exchanging requests with no budget ceiling and no external stop condition. The loop ran for 264 hours and produced a $47,000 invoice, and the post-mortem named two root causes: no per-agent budget cap, and no mechanism that could terminate the session before the next API call completed (The $47,000 Agent Loop).

Why conventional QA misses this: a loop is not an error and not a slow request. Every component is healthy. The only signal is financial, and the financial signal lives in a provider billing console that updates on a delay measured in hours. By the time the spend graph spikes, the night is half over.

Why the loop is expensive, not just long

A traditional service does the same work for the same request every time. An agentic system decides at runtime how much work to do, by looping: think, act, observe, think again. That loop is the entire point, it is what lets an agent handle a task you did not pre-script. It is also a control structure with no natural upper bound on cost, and three properties make it dangerous.

The termination condition belongs to the model, not to you. The agent stops when it believes it is done. If it never reaches that belief, because a tool keeps returning a slightly malformed result, or the goal was subtly impossible, it never stops on its own. Anthropic's own guidance on building agents stresses keeping the loop simple and bounded for exactly this reason: unbounded autonomy and unbounded tool use compound into runaway behavior (Building Effective Agents).

Each iteration costs more than the last. Most frameworks accumulate history: the request, every tool call, every tool result, every reasoning step, all appended and re-sent next turn. So step 50 is not the price of step 1, it is dramatically more, because you pay to re-read 49 turns before producing turn 50. Practitioners measuring this describe the multiplier exceeding 30x by step 50 and 100x in a long autonomous debugging session, since the same system prompt and history are billed on every step (LeanOps on agentic token runaway). The loop is not a flat line. It is a ramp.

Tool results are large and adversarial-by-accident. An agent that calls a search tool and gets back 40KB of HTML has just pinned 40KB into its own context for the rest of the session. Do that a dozen times and it is dragging a quarter-megabyte of garbage through every subsequent call.

Put numbers on the overnight bill

Take Claude Sonnet at roughly $3 per million input tokens and $15 per million output. A healthy turn might send 8,000 input and generate 500 output, about $0.03. Trivial; run thousands and barely notice. Now break the loop: each turn appends a fat tool result, say 3,000 tokens of context growth per iteration. By iteration 40 the context is over 120,000 tokens, each turn costs roughly $0.36 in input alone, and the agent fires one every few seconds, on the order of $200-$400 per hour, per stuck session. One bad loop overnight is a four-figure incident. Ten of them, because the bug shipped to all users at once, is the five-figure Monday morning. The reported cases bracket this exactly: a developer leaving an agent over a long weekend returning to a $4,200 bill on the low end, the $47,000 ping-pong on the high end.

Why your existing guardrails miss it

You almost certainly have rate limits. They do not help, because the agent is not making an abnormal number of requests, it is making a normal number of increasingly expensive ones. A request-per-minute limit treats a 200-token call and a 200,000-token call as identical. We dissect why requests are the wrong unit in Token-Aware Rate Limiting; the short version is that requests are not the thing that costs money, tokens are.

You probably also have timeouts. They are blunt: a 30-second timeout on a single call does nothing about a loop of a hundred fast, legitimate-looking calls. You need a budget on the whole run, not on any call within it. And you have monitoring, which is the problem, because monitoring tells you when something is broken, and a stuck loop is not broken.

The fix: a budget enforced in three places

Give every agent run a hard cost ceiling and enforce it at three layers, because each catches a different failure.

1. A per-run iteration cap. The simplest, most effective guardrail. Most tasks that need 60 steps are tasks that needed 6 and got lost.

MAX_STEPS = 25

step = 0
while not agent.is_done():
    step += 1
    if step > MAX_STEPS:
        raise AgentBudgetExceeded(
            f"halted after {MAX_STEPS} steps without completion"
        )
    agent.take_step()

2. A per-run token budget. Iteration caps miss the case where each step is individually huge. Track cumulative tokens, input plus output, across every call, and halt when you cross a threshold set from your unit economics. If a conversation is worth $0.20 to you, do not let one run spend $40.

BUDGET_TOKENS = 200_000

spent = 0
for step in agent.run():
    spent += step.usage.input_tokens + step.usage.output_tokens
    if spent > BUDGET_TOKENS:
        agent.halt(reason="token budget exhausted")
        break

3. A per-session and per-tenant cap, enforced outside the agent. The first two live inside the agent process, so a bug in the agent can bypass them. The last line of defense lives in a gateway that tracks spend per session, user, and tenant in a shared store and returns an error instead of forwarding the call when a session crosses its cap. It does not trust the agent at all, which is exactly why it survives an agent bug. This is the pre-execution enforcement the $47,000 post-mortem said was missing: a stop that fires before the next API call, not an alert that fires after.

The three layers fail independently. The iteration cap is defeated by giant single steps. The token budget is defeated by a counter-reset bug. The external gateway is defeated by nothing the agent does, but it only knows totals, not intent. Layered, they cover each other, which is the whole point.

Loop detection: catch the spin before the cap

A budget stops the bleeding; detection stops it sooner and tells you why. The cheapest reliable detector is repetition: if the agent calls the same tool with near-identical arguments more than N times, it is almost certainly stuck. Hash each call and keep a ring buffer.

recent = collections.deque(maxlen=6)

def guard(tool_name, args):
    sig = hashlib.sha256(
        (tool_name + json.dumps(args, sort_keys=True)).encode()
    ).hexdigest()
    if recent.count(sig) >= 3:
        raise LoopDetected(f"{tool_name} called identically 3x")
    recent.append(sig)

This catches the most common real loop, an agent that keeps retrying the same failing action because the failure does not change its reasoning, the exact Analyzer/Verifier shape from the $47K incident. It will not catch a loop that varies its arguments slightly each time, which is why the hard budget stays underneath it.

Make the budget a first-class signal

When a run halts on budget, that is a product signal worth emitting loudly: a structured log line, an alertable metric, and a user-facing message. A run that hits its ceiling is telling you either that the ceiling is too low for the real workload, or that you have a class of task that loops. Track budget_halts_per_1000_runs as a KPI; if it rises, something upstream changed, a tool got slower, a prompt got worse, a new cohort is asking harder questions. The halt is your canary.

You cannot test a loop you never simulate

Here is the part teams skip. They add the budget, ship, and never test it, because testing it requires deliberately building a stuck agent, and nobody wants to build a broken thing on purpose. But the stuck agent is the only scenario that matters. A guardrail you have never triggered is a guardrail you do not know works. We have seen production caps off by a factor of a thousand (tokens vs thousands of tokens), discovered only during a real incident because the suite never drove a run to its ceiling.

Treat "agent fails to terminate" as a first-class test case. Build a deterministic fake tool that always returns a slightly-wrong result, point the agent at an impossible goal, and assert the run halts, on iteration count, on token budget, and at the external gateway, independently. Assert the cost ceiling is the number you think it is. Assert the loop detector fires. Then assert the failure is observable: the right log, metric, and message. This is the failure mode that hides between unit and integration tests, the unit tests pass because each part works, the integration tests pass because the happy path completes, and the loop lives in the space between, a system of correct parts that is, as a whole, on fire. Driving the agent into that corner under realistic concurrency, with cost and token accounting on the wire, is precisely the gap browser-native, cost-aware load testing exists to close.

This is the gap Hit was built to own: fire real bursts at your agent endpoint, read the streamed token usage, and assert on a per-run cost ceiling so a runaway loop is a red build instead of a 3am invoice.

The night you do not get paged

The win condition is anticlimactic. One night an agent gets stuck, tries the same broken thing three times, the detector fires, the run halts, a metric ticks up by one, and the user gets a graceful message. Cost: a few cents. Nobody is paged. Someone notices the next afternoon and says, "huh, we caught nine of those last night." That is the goal, not an agent that never gets stuck (impossible; agents get stuck, that is the nature of letting a model decide when it is done) but an agent that gets stuck cheaply, loudly, and on your terms. The loop is going to happen. The only question is whether it costs you a few cents and a log line, or a weekend and $47,000.

Pressure-Test Your AI Before Production Does

Hit fires browser-native, streaming-aware load at your LLM and API endpoints, TTFT, inter-token latency, tokens/sec, and cost per request, with no account and no script.

Try Hit Free →
Sofia Reyes Sofia Reyes writes about AI quality engineering at alt.qa, built by TheWorkCompany.