[KongchangAI]
· 2 min read· 1,159 words

An Unattended Claude Loop Burned 13% of My Quota Overnight — Here Are the Three Safeguards I Added

An Unattended Claude Loop Burned 13% of My Quota Overnight — Here Are the Three Safeguards I Added

An unattended LLM loop with no exit condition burned 13% of plan quota overnight; the author shares three hard-limit safeguards.

The author recounts a real incident: an LLM loop with no stop condition ran unattended all night, raising Claude plan usage by 13 percentage points. The article first explains the shared mechanics of Claude plan limits — the chat app and Claude Code share the same pool, and the terminal usage breakdown excludes app-side consumption; then notes that switching models only helps when prompts name a specific model. The root cause is identified as a missing exit condition; switching to a cheaper model only burns more slowly. The solution is at least two hard limits: consecutive failures, total calls, and wall-clock time, with guards tested in advance. The article also warns that 429 cooldown is a recovery mechanism, not a limit, reviving the loop in each new window. It ends with a five-item pre-flight checklist.

An Unattended Claude Loop Burned 13% of My Quota Overnight — Here Are the Three Safeguards I Added

An LLM loop with no stop condition ran by itself overnight on a subscription account, and when I woke up, plan usage had dropped 13 percentage points. This isn't a pricing problem — the code simply had no logic to end the loop, and nobody was watching. The author did a post-mortem on this incident on dev.to, explaining how Claude plan limits work, how to read usage, and the three safeguards they now add to every unattended loop.

Claude plan usage screenshot

Limits Are Shared — the Chat App Is Eating Your Quota Too

Claude Code is included in Pro and Max subscriptions, and limits consist of two meters: the 5-hour session limit and the 7-day weekly limit. According to Anthropic's help page, the Claude chat app and Claude Code share the same pool — what you spend in the chat window is the headroom Claude Code can use, and vice versa.

This is exactly why you see an "unusually high" usage bar in the morning but can't explain it from the terminal. If part of the usage comes from the chat app, it won't appear in the terminal's usage breakdown.

There are two easy-to-misunderstand details when you hit the limit. Switching models only helps for prompts that name a specific model (like the Opus-only limit); for generic prompts that apply to "all models," switching models changes nothing. The options are to wait, enable usage credits, or auto-continue after reset — the latter is configured via /rate-limit-options in v2.1.234 and above, and organizations use the autoContinueAtUsageLimit setting.

Enabling usage credits has a side effect: prompt cache lifetime drops from 1 hour to 5 minutes, making the same working habits more expensive. Also, the monthly API credits that come with Max can't be used to extend Claude Code limits.

How to Read /usage Correctly

Type /usage in Claude Code. As a subscriber, ignore the Session block and look at the plan usage bar and the breakdown beneath it. The breakdown has three parts: attribution (usage share by skill, subagent, plugin, and MCP server), behavior markers (like long context and cache misses), and loops (number of runs and tokens per scheduled task, requires v2.1.242 or newer).

Press d for the last 24 hours, w for the last 7 days. One limitation: these numbers come from local records on your machine and don't include usage from other devices or the claude.ai app. This means the numbers on screen sometimes can't fully account for that high usage bar in the morning.

Prompt Caching is an optimization mechanism in the Anthropic API and Claude Code: identical prefix prompts (system prompts, long documents, tool definitions, etc.) reused within a short window are cached, and subsequent requests can reuse them directly, significantly reducing latency and token costs. The cache has a lifetime, about 1 hour by default, after which it must be rewritten. Enabling usage credits compresses that lifetime to 5 minutes, meaning the same workflow loses cache hits very quickly and actual costs rise accordingly. This also explains why "buying quota" doesn't equal "keeping the same price" — it changes not just available quota but billing efficiency.

What That Overnight Incident Actually Looked Like

On 2026-08-01, an LLM loop ran all night without human approval, and plan usage rose 13 percentage points. This 13 is a reading from the usage meter — the before-and-after difference. The author deliberately did not convert it to token counts, because any conversion factor is just an estimate.

The root cause was that the loop couldn't stop. If someone had been watching, it would have been killed within minutes. Switching to a cheaper model wouldn't solve anything either — the missing exit condition is still there, it just burns more slowly.

Wall-clock time refers to the actual elapsed time from task start to now, independent of CPU time or token counts. In LLM loops, it's the most intuitive and most easily overlooked guard dimension: a loop might make few calls, but each one triggers long context or multiple retries, and call counting alone can't stop it. Setting a wall-clock limit (e.g., 3 hours) catches this kind of "slow leak." It complements the consecutive-failure limit and total-call limit, and together the three cover error-type, volume-type, and time-type runaway paths.

Three Safeguards for Your Loop

Example limits: 3 consecutive failures, 200 calls, 3 hours

Pick at least two and put them in the code: consecutive-failure limit, total-call limit, wall-clock time limit. The author's own values are 3 consecutive failures, 200 calls, and 3 hours.

import time

MAX_FAILS, MAX_CALLS, MAX_SECONDS = 3, 200, 3 * 3600

def run(task, step):
    start, calls, fails = time.time(), 0, 0
    while not task.done():
        if fails >= MAX_FAILS:
            return "stopped: 3 consecutive failures"
        if calls >= MAX_CALLS:
            return "stopped: 200 calls"
        if time.time() - start >= MAX_SECONDS:
            return "stopped: 3 hours"
        calls += 1
        try:
            step(task)
            fails = 0
        except Exception:
            fails += 1
    return "done"

Checks run before each iteration, and the function returns the stop reason for logging. Test these guards first: pass a function to step that always throws, and confirm you get the failure message after the 3rd attempt.

One trap: 429 cooldown isn't a limit but a recovery mechanism, because it revives the loop in each new limit window. It keeps the loop running politely forever.

429 cooldown (rate-limit backoff) refers to the strategy where the client pauses for a while after receiving HTTP 429 "too many requests" and then retries. Its design goal is to handle brief congestion, not to terminate tasks. When embedded in an unattended loop, the loop automatically resumes after the limit window resets, forming an infinite "hit the wall — wait — continue" cycle. Therefore cooldown logic must be paired with hard limits: cooldown only decides "when to retry," the limit decides "when to give up entirely." Conflating the two is like installing a perpetual restart machine on your loop.

Pre-Flight Checklist Before Unattended Runs

  1. Write down expected usage before starting.
  2. Get human approval for that estimate.
  3. Set at least two limits and test them.
  4. Log the stop reason and usage on exit.
  5. Audit all cron jobs, scheduler entries, and background runners monthly.

These guards only cover loops you write yourself. For Claude Code's built-in /loop, check the Loops row in /usage, and manually disable it or lengthen the interval. And because app usage isn't in the breakdown, that high usage bar in the morning can't always be attributed from the screen alone.

This article comes from a dev.to author's post-mortem of a real incident; the full version (including FAQs on limits, credits, and model switching) is on the original blog. For developers who regularly run unattended tasks, the lesson is direct: the problem isn't that the model is expensive, it's that the loop won't stop itself.

#Claude Code#LLM loop#usage limits#unattended automation#prompt caching
Share:

Related articles