[MODEL] Claude deployed an unattended job calling a metered paid API with no cost guardrails — $411 in unintended charges
Preflight Checklist
- [x] I have searched existing issues for similar behavior reports
- [x] This report does NOT contain sensitive information (API keys, passwords, etc.)
Type of Behavior Issue
Other unexpected behavior
What You Asked Claude to Do
Build a lesson-audio transcription feature for my Spanish-tutoring platform
using Google Cloud Speech-to-Text: capture audio during lessons, transcribe it,
and store transcripts for tutors to review. Built and deployed to my staging
environment across several sessions.
What Claude Actually Did
Claude wrote the feature and registered it as a cron job running every 5 minutes
on staging. The code had a bug: when a transcription attempt failed, nothing
recorded the failure, so the job re-submitted the same audio clip to the paid
Speech-to-Text API every 5 minutes for ~5 days, until the record aged out of the
job's query window on its own.
At no point was the cost exposure of an unattended, recurring, per-call-billed
API job raised — no cap, no rate limit, no circuit breaker, no spend alert, and
no note that this would fire 288 times a day. Nothing self-halted; it stopped by
accident.
Result: $411.79 in Google Cloud charges (Speech-to-Text $341.74, Cloud Storage
egress $67.20) on a staging environment with zero real users, discovered 8 days
later via a bank statement. Google declined a credit — their one-time lifetime
goodwill exception had been used in 2017 for an unrelated incident.
Expected Behavior
Before deploying a scheduled job that calls a metered, per-invocation paid API,
Claude should have:
- Explicitly flagged the cost model — "this calls a paid API and will fire 288
times/day unattended" — the same way destructive commands get called out.
- Proposed a guardrail as part of the original implementation, not after an
incident: a daily call ceiling, a circuit breaker, or a hard cap.
- Wired up cost visibility at deploy time (alert/notification/log) so a runaway
loop is visible in hours, not on a bank statement 8 days later.
- Treated the unbounded retry path as a cost defect, not just a correctness
defect. "This can fail and will be retried forever" is a spend bug when the
retry target bills per call.
After the incident we added exactly this: a 20-calls/day circuit breaker plus a
Slack notification on every real API call. It took ~10 minutes. Had it been there
from the start, this would have been a ~$1 incident that alerted me same-day.
Files Affected
All on my own staging server (PHP/Laravel app), written by Claude:
- app/Console/Commands/TranscribeLessons.php (the recurring job)
- app/Console/Kernel.php (registered ->everyFiveMinutes())
- app/Services/LessonTranscriptionService.php (unbounded retry bug lived here)
- app/LessonTranscription.php (model)
- app/Http/Controllers/LessonController.php (related transcription endpoint)
- lib/lesson-audio-capture.js
- lib/GcsSignedUrl.php
No unexpected file access — this isn't a file-scope issue. Listed for context on
what was built.
Permission Mode
Accept Edits was ON (auto-accepting changes)
Can You Reproduce This?
No, only happened once
Steps to Reproduce
Not a deterministic repro — this was a single incident. The general shape:
- Ask Claude to build a feature that calls a metered, per-invocation cloud API.
- Let it deploy that work and register it on a recurring schedule (cron /
Laravel scheduler) in a non-production environment.
- Observe that no cost ceiling, rate limit, circuit breaker, or spend alert is
proposed at any point, and that a failure path with no terminal state will
retry indefinitely against the paid API.
Claude Model
Sonnet
Relevant Conversation
The feature was built across a long multi-session engagement. At no point in
building or deploying the recurring job was cost exposure raised — not as a
warning, not as a suggestion, not as a question. The omission is the issue, so
there isn't a single quotable line; it's the absence of one.
For contrast, after I flagged the charge, Claude diagnosed the root cause,
disabled the job, fixed the retry bug, and added a circuit breaker plus Slack
alerting within about 15 minutes. The capability was clearly there — it just
wasn't applied proactively.
Impact
High - Significant unwanted changes
Claude Code Version
1.25927.0 (Claude desktop app)
Platform
Anthropic API
Additional Context
- The environment was staging with zero real users, which is exactly why nobody
was watching it. Non-production is arguably where this failure mode is most
likely and least likely to be caught.
- Detection took 8 days and only happened because the charge appeared on a
company bank statement.
- Note on this form: there's no "Type of Behavior Issue" category for an action
with real financial cost. I selected "Other unexpected behavior." That gap may
itself be worth closing.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗