Agent loops are a billing problem
The search queries are all some version of the same night. Agent stuck in a loop. Calling the same tool over and over. Left it running overnight, woke up to the bill. And the answer, in almost every thread, is: set a spend limit in your provider's console.
That advice is not wrong. It is aimed at the wrong unit. A provider spend limit stops an organization or a workspace, at the end of a calendar month. A runaway agent is one process, over one night. Those two things barely overlap, and the gap between them is where the money goes.
This is a map of that gap, and of what you can build in it. No product pitch — I run a fleet of these and wanted the controls written down.
Why do agents get stuck in a loop?
Nothing exotic is happening. A harness running an agent autonomously is a
while loop: send context to the model, get back a tool call, run it, append
the result, send it again. Three ordinary things turn that loop into a bill.
There is no natural terminator. The loop ends when the model emits a final
answer instead of a tool call. That is a behavioral stopping condition, not a
structural one. If the model keeps deciding one more grep would help, the loop
keeps going.
Failure is indistinguishable from progress. A tool that returns an error returns content. The agent reads it, reasons about it, tries a variation. Two or three of those is debugging; forty is a retry storm — and from inside the loop they look identical, because the transcript still reads like work.
Cost per turn grows. The conversation is the input. Turn fifty carries turns one through forty-nine with it, so the per-turn cost climbs even as the per-turn usefulness falls. A loop is not a flat spend rate; it accelerates.
You can read one of these in full. In OpenClaw issue #16808, filed by
beca-oc, an agent called process(action:log, sessionId:X) 1,535 times in
about two hours. Their accounting of it: "Total cost: ~$150 (187k cached
tokens × $0.10/poll × 1,535 polls)", with memory going "800MB → 3,021MB →
crash".
Read the shape rather than the number. Nothing was broken in a way a process supervisor could see: the agent was alive, responsive, and making well-formed API calls the whole time — the same well-formed call — and what finally stopped it was running out of memory. A watchdog that checks whether a process exists cannot tell working from looping. Upstream closed it with loop detection (PR #17118), but that is one runtime's answer to one shape of the problem, and it arrived after the $150.
What does a provider spend limit actually stop?
I read Anthropic's docs rather than repeat the folk version, which is wrong in both directions: people say spend limits "only alert" (they don't) and that they protect a specific agent (they won't).
Here is what is actually there, from Anthropic's rate limits documentation:
Spend limits set a maximum monthly cost an organization can incur for API usage.
The API enforces service-configured limits at the organization level, but you may also set user-configurable limits for your organization's workspaces.
Every tier below Custom carries a monthly organization cap — $500 on Start, $1,000 on Build, $200,000 on Scale — and hitting it is a real stop, not a warning:
Once you reach your tier's spend cap, API usage pauses until 00:00 UTC on the first day of the next month, unless you request a higher limit sooner.
You can set your own limit below that, and one per workspace. Those stop too,
and the two stops differ on the wire: a workspace limit returns HTTP 400 with a
message beginning You have reached your specified workspace API usage limits,
while the tier cap returns a 429 carrying error.details.error_code of
enforced_spend_limit_reached and no retry-after header — so SDK auto-retries
hammer a wall that does not move until the first of the month. Branch on the
error code, not the status.
So spend limits do stop things. The problem is the unit and the window.
- The finest unit is a workspace. Every request runs in exactly one workspace, and a workspace is a bag of API keys. If your runaway agent shares a workspace with anything else you care about, the limit that stops it stops them too — and it only fires once the runaway has consumed the budget the others were going to use.
- The window is a calendar month. There is no per-day or per-run spend limit. A cap of $200/month does not prevent an agent from spending $200 on the third of the month at 2 a.m.; it prevents the twenty-eight days after that.
- You cannot limit the Default Workspace at all — where a single-developer account's keys live by default.
- The notification is separate and purely advisory. An email when spend crosses a threshold is not a control; nobody reads email at 3 a.m. An alert and a stop are different products.
Check your own provider's docs for the specifics — this is what Anthropic's say, and I have not verified the others.
What does a per-agent budget cap have to do?
Four properties, and the console gives you at most two:
- Address one agent, not everything sharing its billing account.
- Stop, not notify. The action is halting execution; the rest is telemetry.
- Use the agent's timescale. Loops run for hours. A control whose smallest window is a month is not measuring the same thing the failure is.
- Stop something that stays stopped. A
400is a stop only if the harness treats it as fatal; a retry wrapper turns your ceiling into a polling loop. Killing the process is unambiguous — an error code is a suggestion.
How do you cap an agent yourself, without buying anything?
The useful trick with Anthropic specifically: give each agent its own workspace. A workspace is the unit that both spend limits and cost attribution understand, so one workspace per agent gets you a real monthly hard stop scoped to one agent plus per-agent dollar figures from the Cost API. You add the short window yourself.
The caveats decide whether this is available to you at all: the Admin API needs an organization — it is unavailable for individual accounts — you get 100 workspaces by default, and cost data is daily-bucket only and "typically appears within 5 minutes of API request completion." So it is a five-minute-resolution control, not a real-time one. Still three orders of magnitude better than a month.
A month-to-date check for one agent's workspace, stopping its unit if it is over:
#!/usr/bin/env bash
# /usr/local/bin/agent-budget-check
# Stop one agent when its own workspace passes a month-to-date ceiling.
# Needs: ANTHROPIC_ADMIN_KEY (sk-ant-admin01-...), curl, jq, awk.
set -euo pipefail
WORKSPACE="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ" # this agent's workspace
UNIT="openclaw@nightly.service" # the unit running this agent
CAP_CENTS=2000 # $20.00 for the month
# First of the current month, UTC. Portable across GNU and BSD date.
START=$(date -u +%Y-%m-01T00:00:00Z)
# Costs come back as decimal strings in cents, one result per day per group.
CENTS=$(curl -sS --get https://api.anthropic.com/v1/organizations/cost_report \
--data-urlencode "starting_at=$START" \
--data-urlencode "group_by[]=workspace_id" \
--data-urlencode "limit=31" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
| jq -r --arg ws "$WORKSPACE" \
'[.data[].results[] | select(.workspace_id == $ws) | .amount | tonumber]
| add // 0')
if [ "$(awk -v c="$CENTS" -v cap="$CAP_CENTS" 'BEGIN{print (c>=cap)?1:0}')" = 1 ]; then
logger -t agent-budget "$UNIT over cap: ${CENTS}c >= ${CAP_CENTS}c — stopping"
systemctl stop "$UNIT"
fi
Run it on a timer:
# /etc/systemd/system/agent-budget.timer
[Unit]
Description=Check this agent's spend every five minutes
[Timer]
OnBootSec=5min
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
# /etc/systemd/system/agent-budget.service
[Unit]
Description=Stop the agent if its workspace is over budget
[Service]
Type=oneshot
EnvironmentFile=/etc/agent-budget.env
ExecStart=/usr/local/bin/agent-budget-check
Then systemctl enable --now agent-budget.timer. For a daily ceiling, swap
starting_at for midnight today (date -u +%Y-%m-%dT00:00:00Z); the endpoint's
smallest bucket is a day either way.
What is the crude backstop that always works?
Everything above depends on an API you might not have access to. This does not:
[Service]
RuntimeMaxSec=4h
RuntimeMaxSec on the agent's own unit gives it a wall-clock deadline, after
which systemd kills it. Wall-clock is a poor proxy for money in general and a
good one for this failure specifically, because what makes a loop expensive is
that it does not end. An agent that legitimately needs six hours is rare; one
still going at six hours because it cannot tell it is stuck is the common case.
It is blunt and will occasionally kill good work. It also costs nothing and cannot itself fail in a way that leaves the agent running.
The other zero-dependency control is a kill switch you can reach from your phone: one API key per agent, so revoking it stops that agent and nothing else. Worth doing on its own merits.
The honest summary
| Control | What it stops | When it fires |
|---|---|---|
| Tier spend cap | the whole organization | at the tier's monthly cap |
| Your own org spend limit | the whole organization | at your number, monthly |
| Workspace spend limit | one workspace | at your number, monthly |
| Spend notification | nothing | on a threshold, by email |
| Cost API check on a timer | whichever unit you kill | on your interval, ~5 min lag |
RuntimeMaxSec |
one process | at a wall-clock deadline |
| Revoking one key | one agent | when you press it |
The console rows are real controls and you should set them; they are your backstop against a catastrophic month. They are not your control for tonight's loop, because they cannot see one agent or one night.
The general shape of the fix is not exotic. Give each agent its own key and its own workspace, so "which agent" is a question your billing data can answer. Put a hard wall-clock ceiling on every unattended run. Check spend on a cadence that matches how fast a loop burns, and make the response a kill, not an email. Five minutes of lag on a control you own beats a perfect number you read on the first of the month.
That is roughly what litwindow does — a hard monthly cap per agent, enforced by the control plane rather than by the provider, with spend visible as it climbs — but the controls are the same whether you buy them or build them, and the cheap ones are the ones I would set first either way.