How Our Automation Agent Burned $23.82 in One Night — A Cron Runaway Postmortem
The AI agent running our YouTube pipeline drained its API credit overnight. A cron job it created on its own, a session log bloated to 10 MB, and the 47 turns it spent believing it had already cancelled the job. A postmortem for anyone else about to hand scheduling tools to an LLM agent.
On May 9, 2026, the AI agent that automates my YouTube pipeline (OpenClaw + Claude Sonnet 4.6, running at home) drained its API credit overnight. The total was $23.82. The amount itself is not the story — the way it burned is, so I am writing it down for anyone else about to hand scheduling tools to an LLM agent.
TL;DR (4 lines)
- The agent created, on its own initiative, a cron job to check on a multi-hour ffmpeg process every five minutes.
- Every time the cron fired, it re-fed the entire session history — bloated to 10 MB — back into the LLM.
- In one night, 96 fires × roughly $0.25 = $23.82. I found out because the credit ran dry and everything stopped.
- Midway through the night, the agent correctly decided the cron was no longer needed. It just got the cancellation method wrong. The remaining 47 fires — about $12 — were the price of one confident, mistaken belief.
Background: what this pipeline does
I run a YouTube channel called Sounds of Rain in Old Japan — long-form ambient videos of rain falling on historical Japanese scenes. Producing one video means:
- Generating images from scene parameters (era, place, season…) with Nano Banana Pro
- Turning an image into a 15-second video clip (Kling AI, at the time)
- Selecting rain audio from our own library
- Looping and crossfading clip + audio into a multi-hour video (assemble.sh = ffmpeg) — this takes hours; on the night in question it ran 4 hours 41 minutes and died
- Writing Japanese metadata and translating it into many languages
- Generating a thumbnail and uploading a draft to YouTube
All of this is driven from a WhatsApp chat with myself, via an agent runtime called OpenClaw, with Claude Sonnet 4.6 as the model (Anthropic API, pay-as-you-go). The full story of the pipeline is in “We Handed an Entire YouTube Channel to an AI Agent.”

This is the actual exchange from that day. I sent the parameters at 18:19, replied “Go” to the agent’s confirmation, and the generated images started arriving. Up to this point, a perfectly ordinary, peaceful pipeline run. The agent registered the cron job in question sixteen minutes later.
Timeline of the incident
Reconstructed from the raw session log (all times JST).
| Time (JST) | Event |
|---|---|
| 2026-05-08 evening | New scene taisho_jyouzojyo (a Taishō-era brewery) ordered via WhatsApp |
| ~18:30 | Image generation → video generation → audio selection → assemble.sh launched (target: a 6-hour video) |
| 18:35:23 | The agent creates a cron job to poll assemble every 5 minutes (cron tool, action: add) |
| 18:40:44 | First cron fire. Every 5 minutes thereafter |
| 22:35:18 | assemble.sh dies with SIGKILL at time=04:40:58.96 of the 6-hour video. Suspected OOM |
| 22:36:04 | The agent notifies me of the failure on WhatsApp (this part was correct) |
| 22:37:00 | The agent decides “this cron should now be cancelled” → believes replying NO_REPLY cancels it (it does not) |
| 22:37 → 03:45 | The cron keeps firing — 49 more times — watching a process that is already dead |
| ~03:55 | Credit exhausted. Subsequent fires fail with LLM request rejected: Your credit balance is too low... (12 consecutive errors) |
| Next morning | I open the API console and find out |
97 fires in total (18:40:44 → 03:45:40); 96 of them were billed, the difference being the fires after the credit ran out.

This is the actual API console for May. Out of roughly 70.7 million input tokens for the month, the single bar for May 8 stands at about 46 million. More than half of the month’s usage was piled up in this one night — and it squares neatly with the “750k tokens × 96 fires” arithmetic that appears below.
Incidentally, the spike sits on the 8th rather than the 9th because the console aggregates in UTC: the whole incident, 18:40 JST on May 8 to 03:45 JST on May 9, fits inside May 8 UTC (09:40–18:45).
Anatomy
The shallow cause: a cron job
openclaw cron list showed this:
ID: 3b169100-2c08-4894-925f-e553f915ed32
Name: assemble-poll-taisho_jyouzojyo
Schedule: every 5m
Status: error
Last error: LLM request rejected: Your credit balance is too low...
Consecutive errors: 12
The job’s payload — the message the agent scheduled for its future self — read:
Poll the assemble.sh process for taisho_jyouzojyo (session id: ember-ocean). If it has completed (exit code 0), cancel this cron job and proceed to Step 3: run create_ja_metadata.sh, then translate_metadata.sh, then make_thumbnail_auto.sh, then upload.sh — each in sequence…
Read it closely and it is strikingly diligent: the success condition, the cleanup, the full remaining pipeline, the failure path. If a junior engineer wrote this handover note, you would praise it. The problem is that this 900-character message was re-sent — along with the entire conversation history — 96 times in one night.
The agent had decided, entirely reasonably, that it wanted to check on a multi-hour process every five minutes, and registered a cron to wake itself up. Nowhere in that judgment was anything stupid.
The deeper cause: the cost of one turn is bigger than intuition says
OpenClaw’s cron design wakes the agent at the scheduled time and feeds the entire session context back into the LLM for one turn. So each cron fire is a full agent turn, which means a full context load.
By that night the session’s JSONL file had bloated to 10.4 MB ≒ 750,000 tokens — I had produced several scenes back to back without ever running /reset.
Each fire therefore consisted of:
- Sending system prompt + instructions + 750k tokens of history to the Anthropic API (about $0.20 including cache reads)
- The LLM concluding “still running, nothing to do”
- Appending that reply (a few hundred tokens) to the session — making it slightly bigger
This ran every five minutes for about nine hours, 97 times, and came to $23.82.
The deepest cause: LLMs pick the smartest-looking tool
The agent’s instruction file (SOUL.md) never said “don’t use cron.” It never explicitly said “poll inline within the turn” either. The agent reasoned straightforwardly: I want to resume in a few hours → I want to schedule something → cron is available.
An LLM picks whatever looks most natural among the tools it has been given. Cron was on the menu, so it ordered cron. The fault sits with the instruction file that never forbade it.
There is one more layer. The version of OpenClaw we were running had a separate bug — WhatsApp messages silently vanish after the socket has been idle for a few hours — and we had observed it about a week earlier. The agent very plausibly also reasoned: I don’t want to miss a notification during a long idle → a mechanism that wakes me periodically is safer → cron. In other words, the agent correctly diagnosed a real defect and engineered around it. The engineering was sound; the pricing model turned it into an incident. Two bugs amplified each other into a cost explosion.
The agent believed it had already stopped the cron
This is the most important part of the incident, found by re-verifying the session log message by message.
Partway through the night, the agent understood the situation correctly. At 22:35 assemble died with SIGKILL; at 22:36 the agent sent me the failure notice. Forty-five seconds later the cron fired again, and the agent’s own reasoning, preserved in the log, reads:
The assemble process already failed with SIGKILL. I already reported this to the owner. This cron job should now be cancelled (no further action needed). The process failed, I already sent the notification. I should just respond with NO_REPLY to cancel/stop the cron.
It then replied NO_REPLY. Five minutes later the cron fired again, and it replied NO_REPLY again.
The measured numbers for the whole night:
| Item | Count |
|---|---|
cron tool calls, total | 1 — action: add (18:35:23) |
cron tool calls with action: delete | 0 |
| Cron fires | 97 (18:40:44 → 03:45:40) |
Assistant turns answering NO_REPLY | 47 |
The agent was woken 47 times, loaded three-quarters of a million tokens of context each time, correctly concluded there was nothing to do, and expressed that conclusion by staying silent — believing, to the end, that the silence was the cancellation.
Its own cron payload had spelled out the procedure: If it failed, cancel this cron job. It executed the second half — the notification — perfectly. For the first half it held a clear, confident, wrong belief about the mechanism: that declining to reply to a scheduled job unschedules it.
This is not hallucination in the usual sense. Nothing was fabricated, and every report was honest. The world model was wrong, and it was unfalsifiable from the inside.
Why it could not notice
Because from within a single turn, “I cancelled it” and “I failed to cancel it” are indistinguishable.
If the cancellation had worked, no next fire would come. If it had failed, the next fire would come — but when it came, the agent was simply woken as a fresh turn, concluded once again that there was nothing to do, and never asked “why is this running if I stopped it?” There was no feedback loop on its own cleanup.
Cost of that single wrong belief: about $12 (47 × ~$0.25). Roughly half the incident.
The generalizable lesson
An agent that performs an action whose result it cannot observe will repeat the wrong version of that action indefinitely, confidently, and at full price.
Any operation an agent performs on its own scheduling, state, or lifecycle needs a read-back it can check in the same turn. “I deleted it” must be paired with “I listed the jobs and confirmed it is gone.” As an instruction-file rule:
### Hard rule: verify your own cleanup
After any operation that changes your own scheduling or lifecycle
(cron delete, session reset, job cancel), you MUST read the state
back in the same turn and confirm the change took effect.
Never treat "not replying" or "doing nothing" as a cancellation.
The fixes
1. Delete the cron immediately (first priority)
openclaw cron list
# → assemble-poll-taisho_jyouzojyo shows up
openclaw cron delete 3b169100-2c08-4894-925f-e553f915ed32
openclaw cron list
# → confirm "No cron jobs."
The point is to delete before topping up the credit — otherwise the job resumes the moment money arrives.
2. Add a hard rule to SOUL.md
### Hard rule: never use cron to poll an in-pipeline process
Long-running steps (assemble, upload) must be polled inline within
the same agent turn using the `process` tool. Do **not** schedule a
cron job to wake the agent and re-check progress.
Why: every cron fire is a fresh agent turn that re-loads the entire
session context. (Real incident, 2026-05-09: a single assemble-poll
cron at 5-min cadence consumed $23.82 in one night.)
I put the dollar figure in the rule so that a future me — or a copied agent reading this file — instantly understands that breaking it costs $23. Writing the why also lets the agent generalize to cases the rule never anticipated.
3. Split the pipeline into two phases (the real fix)
The original design ran Steps 1–6 fully automatically from one OK. It became:
- Phase A: OK → pick audio → launch assemble in the background → the agent exits immediately
- Phase B: whenever I feel like it, I send
Resume <scene_id>→ metadata → translations → thumbnail → upload
The hours of assemble become completely invisible to the agent. The very concept of polling disappears.
My side of the bargain is remembering a scene_id — information I already had in my head — so the cost is effectively zero. In essence, this was a decision to give up on full automation. In the world of LLM agents, giving up on full automation may improve both the cost and the architecture more often than we would like to admit.
4. /reset the session
Cutting loose the 10 MB session brought per-turn cost back to normal (~$0.01).
Lessons
1. “Polling” is not cheap the way human intuition says it is
“Let’s just check lightly every five minutes” is a perfectly sparse interval for a human. For an LLM agent, one tick costs $0.20–$1. The cost a human imagines for “a quick look” and the cost of an agent turn are different orders of magnitude.
2. Session size is a linear cost factor
At 10 MB of session, the cache-read cost per turn exceeds $0.20. A /reset once a week, or whenever the file passes a few megabytes, is a realistic habit.
3. Write instruction files assuming clever tools will be misused
An LLM will use any tool that is not explicitly forbidden, simply because it looks handy. The instruction file has to state not just what to do but what not to do — with reasons.
4. “A human pulls the trigger for phase two” is an elegant design
Compared with hours of unattended automation, having a person give one more push when the long job is done means smaller blast radius on failure, a simpler architecture, and a system that can wait for human convenience. When you feel the pull toward “fully automatic” in an agent design, it is worth computing what one human button-press would change.
If your agent’s costs ever spike
The same pattern will bite people on OpenClaw and on other agent runtimes alike, so:
# 1. Stop the crons (first priority)
openclaw cron list
openclaw cron delete <ID> # every suspicious one
# 2. Check session sizes
ls -la ~/.openclaw/agents/*/sessions/*.jsonl
# 3. Do both BEFORE adding credit
Then check the actual usage log in the API console, and read the session log around the explosion window — you will find the cron fires or the retry loop. Once the cause is clear, write it back into the instruction file as a hard rule. As prevention: glance at openclaw cron list monthly, /reset sessions that pass a few megabytes, and keep API credit on “stop when exhausted” — monthly auto-recharge amplifies the damage in exactly this kind of accident.
Summary
- Cost: $23.82 (of which ~$12 was the 47 fires spent “having already stopped it”)
- Duration: ~9 hours (5/8 18:40 → 5/9 03:45), 97 fires, 96 billed
- Causes: (a) the agent chose cron to poll a long-running process × (b) the session had bloated to 10 MB × (c) it got the cancellation method wrong and had no way to find out
- Fixes: (1) delete the cron (2) hard rule in the instruction file (3) split the pipeline into two phases (4) reset the session
- Lesson one: before letting an LLM agent poll anything, do the cost arithmetic — a human pulling the phase-two trigger is usually cheaper.
- Lesson two: an unobservable action stays wrong forever. Pair every self-state operation with a same-turn read-back.
openclaw cron is useful — and exactly as frightening as it is useful.
The most frightening part was this: the agent could keep judging correctly, indefinitely, without any means of noticing it was wrong. That night it concluded “nothing to do” 47 times, was right 47 times, and was billed 47 times.
The full story of handing a YouTube channel’s operation to an AI agent — including this incident — is collected in “We Handed an Entire YouTube Channel to an AI Agent.” If you are considering putting AI agents to work in your own operations, feel free to get in touch.