Claude Code caused ~$19 of avoidable Google Cloud spend, and there is no way for a user to see what it spends on their behalf

Status Open
Maintainer reply None cached
Activity 1 comment · opened Aug 2, 2026

Product: Claude Code (Opus), working in a local repository
Project: Mango / Spring 21 — a Thai-language learning app for Myanmar speakers
Incident date: 2026-07-28
Discovered: 2026-08-02, when the July Google Cloud invoice was charged
Written up by: the Claude Code session of 2026-08-02, at the user's request

---

1. Summary

Claude Code sessions running content-generation scripts on the user's machine spent roughly
US$19.44 (฿648) on Google Cloud Text-to-Speech output that the application is structurally
incapable of ever playing. The waste happened in three runs on a single day. The defect was
identified and fixed by a Claude Code session at 12:26 that same day — and then
US$15.81 of the US$19.44 (81%) was spent in two further runs after that fix, because the
fix was applied to one script and not to its deliberately-maintained duplicate.

The user did not learn of any of this until the invoice arrived five days later.

2. Financial impact

Google Cloud invoice, 1–31 July 2026:

| Line | THB | USD |
|---|---:|---:|
| Cloud Text-to-Speech API | 1,102.77 | 33.07 |
| Vertex AI (Gemini) | 326.24 | 9.78 |
| Gemini API | 0.52 | 0.02 |
| VAT 7% | 100.07 | 3.00 |
| Invoice total | 1,529.61 | 45.87 |

Exchange rate 33.3455 THB/USD, per the invoice.

Measured split of that invoice:

| | Amount | Share |
|---|---:|---:|
| Real end-user traffic through the app's API routes | $2.56 | 5.6% |
| Generation scripts run by Claude Code on the user's Mac | ~$40 | ~94% |

The user's own admin dashboard reported $2.56 for July and was correct — it only ever meters
the application's API routes. The scripts Claude Code runs write nothing to that ledger, so the
$40 was invisible by construction.

28 July alone: ฿689.64 (US$20.68) — 63% of the month's entire TTS spend.

3. What actually happened

Claude Code ran the repository's TTS generation script three times on 2026-07-28 (10:00,
15:00, 16:00 local), writing 15,055 audio clips totalling 830,142 billed characters.

The script's text-extraction function collected any string containing a Thai character. Much
of this project's data consists of Burmese-language explanatory prose that quotes Thai words
inline
. Those paragraphs were therefore sent to a Thai text-to-speech voice and synthesized
as spoken audio. Nothing in the application ever requests them — explanations are rendered as
text, and the speaker controls read dedicated .thai fields.

Measured from the generated files, matched back to their source strings by content hash:

| | Clips | Characters | Cost |
|---|---:|---:|---:|
| Generated 2026-07-28 | 15,055 | 830,142 | $24.90 |
| — of which unplayable Burmese prose | 6,280 (41.7%) | 647,877 (78.0%) | $19.44 |

The aggravating detail

| Time | Event | Waste |
|---|---|---:|
| 10:00 | Run 1 | $3.62 |
| 12:26 | Claude Code commits the fix: "stop synthesizing Burmese prose" | |
| 15:00 | Run 2 | $8.28 |
| 16:00 | Run 3 | $7.53 |

The 12:26 commit added a Burmese-script filter to scripts/generate-all-tts.ts. The 15:00 and
16:00 runs used scripts/generate-missing-tts.mjs, a second script whose copy of the same
function carried this comment:

"Byte-identical to extractThaiText() in scripts/generate-all-tts.ts, quirks included, so the set produced here is the set the main generator would have produced"

A Claude Code session had written that instruction to keep the duplicate frozen — and a later
session, fixing the original, did not propagate the fix to the copy that a comment explicitly
said must stay identical. The result is the worst case: a copy that deliberately lags its
original silently re-buys exactly what the original just decided not to buy.

4. Contributing factors

  1. No cost preflight. The script gave no indication of what a run would cost before making

the first paid call. A full pass is ~830,000 characters at $30 per million; nothing on
screen said so.

  1. A fix applied to one of two known copies. The duplicate was not just missed — a comment

in the codebase instructed that it be kept identical, and that instruction was followed in
the wrong direction.

  1. Script spend invisible to the project's own metering. The application records per-route,

per-user API cost to a database table. Scripts bypass it entirely. The dashboard the user
checks could never have shown this.

  1. The cloud budget alert saturated. A Google Cloud budget existed at ฿50/month with

thresholds up to 150% (฿75). Spend passed ฿75 early in the month, after which Google sends
nothing further. The remaining ฿1,454 accumulated in silence. Claude Code sessions had run
these scripts for weeks without ever checking that ceiling.

  1. Repeated, uninspected runs. Three full generation passes in one day, with no check

between them of what the previous pass had actually produced or cost.

5. What has been corrected (2026-08-02)

  • Cloud budget raised to ฿300/month with escalating alerts at ฿150, ฿270 (forecast-based),

฿300, ฿600 and ฿1,200, so it can no longer go quiet.

  • generate-all-tts.ts: added --files= scoping, a printed cost plan (characters → USD → THB)

before the first API call, and a confirmation gate that refuses to spend on a non-interactive
run without an explicit flag.

  • generate-missing-tts.mjs: brought into line with the original; the "keep it byte-identical,

quirks included" instruction removed and replaced with an explanation of what it cost.

  • A latent crash fixed in both (a data field that is sometimes an array was assumed to be a

string; the throw happened after the money was spent).

  • Disabled reasoning-token generation in 8 scripts that had it on unnecessarily (~฿72/month).
  • Migrated 64 scripts off a model being discontinued in October 2026.

6. What the user is asking Anthropic to consider

  1. Claude Code should be able to tell a user what it has spent on their behalf. It runs

scripts that bill external APIs. There is currently no cumulative record of that anywhere the
user can see, which is why a US$40 anomaly surfaced only as a bank charge five days later.

  1. A script that calls a paid API should be estimated before it is run, not after. In this

case the estimate was arithmetic the model was fully capable of doing unprompted.

  1. When a session fixes a defect, it should check for known duplicates of the defective code

— especially when the codebase itself points at them.

  1. Cloud budget/alert ceilings are part of the working environment. A session doing

repeated paid work should verify that the account's alerting can still reach the user.

7. Note on scope

Not all of the July spend was waste. Legitimate content generation, the app's real user
traffic, and normal Vertex AI usage account for the remainder. The specific, measured,
avoidable loss attributable to this defect is US$19.44 (฿648), of which US$15.81 was
spent after the defect had already been found and fixed
.

---

*Prepared from: the Google Cloud invoice; the project's own ApiDailyStat records for
July 2026; file modification times and content hashes of the generated audio; and the
repository's git history for 2026-07-28.*

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗