Verbatim symptom
bills the metered API instead of the Max login# The Child Process That Inherited Our Wallet
The warning was sitting in the CLI output the whole time: "bills the metered API instead of the Max login". I'd read past it, you know, the way you read past a terms-of-service paragraph. Right up until two automatic top-ups landed on the account and the numbers stopped looking like a rounding error.
Here's the post-mortem. How it broke first, the lesson after, no ceremony.
Symptom
The observed behaviour was simple and expensive. The page pipeline in claude-web-api was meant to run on the flat-rate subscription login. Instead it was drawing from the metered API, and the CLI said so in as many words: "bills the metered API instead of the Max login".
The bill climbed fast. Roughly 64 million Opus input tokens went through before anyone noticed, and the account triggered two automatic top-ups along the way. Two. That's the part that got my attention, because a top-up is a safety net doing its job, and a net that fires twice means something is falling continuously, not once.
The whole thing was introduced, detected and fixed on 2026-10-09. Same day, start to finish. I'd love to call that a win, but a short incident is mostly a sign the spending was fast, not that we were.
What we thought it was
Our first diagnosis was wrong, and it was believable, which is the annoying kind of wrong.
I assumed the usage was legitimate. The pipeline generates pages, pages are input-heavy, and Opus is the model you reach for when you want the output to be good. So a big token count looked like a big workload, plausibly a retry loop, or a prompt that had quietly grown, or a batch that fanned out wider than intended. Each of those produces exactly this shape: lots of input tokens, a bill that looks proportionate to the work.
That's the trap. The volume was explainable, so I went looking at volume. I was asking "why so many tokens?" when the better question was "why are these tokens costing money at all?" Under a flat-rate login the same 64 million tokens would have been an unremarkable afternoon. The tokens were never the anomaly. The billing path was.
Anyway, it took a while to turn the question around. Stare at a usage graph long enough and you start optimising the graph.
Root cause
The pipeline spawned claude -p as a child process and handed it the parent environment unchanged. That's the whole bug, honestly, one line of intent: "run the CLI". But a spawned process inherits whatever you give it, and we gave it everything.
Now add Bun. Bun auto-loads the repo's .env file into the process environment, no import, no config, no ceremony. That .env carried the fleet API key. So the parent process held a live API credential that nobody had consciously put there for the pipeline's benefit, and the child inherited it along with everything else.
The CLI then did what it's designed to do. If an API key is present in the environment, it uses it, and the key takes precedence over the subscription login. Credential present, metered billing selected. No error, no prompt, nothing to trip a test. The subscription login was sitting right there, unused, while the key quietly won.
The offending call lives at src/lib/pagePipeline.ts:239@f5467c1^, the spawn as it stood before the fix, and it just passes the environment straight through. After the change the equivalent line sits at src/lib/pagePipeline.ts:241@f5467c1. Two lines apart, which tells you how small the difference between "safe" and "billing a metered account" actually was.
The fix
The fix strips the API key and auth-token variables from the child environment before the spawn. That's it. The child no longer sees a credential that would override the login, so it falls back to the subscription path it was always supposed to use.
It was committed the same day, in f5467c1, and the commit that introduced the behaviour was 3cb63b6. Both on 2026-10-09, which I mention mostly because it makes the gap between cause and cure look absurdly narrow. It was narrow. The expensive part was the stretch in the middle where nobody knew.
I deliberately removed the variables rather than overriding them with empty strings. An empty value can still count as "set" in some tools, and I didn't want to find out which ones the hard way. Deletion is unambiguous: the variable isn't there, so it can't be read.
Why it happened
The systemic cause is that nothing in review could have shown it. The diff for 3cb63b6 looked like what it was, a spawn call. Nobody reviewing a spawn call is asking what the surrounding environment contains, because the environment isn't in the diff. It's ambient. It lives in a .env file the reviewer isn't looking at, loaded by a runtime feature nobody wrote down.
So the bug existed in the gap between three things that were each individually fine: a child process, an inherited environment, and an auto-loaded secrets file. Each one is reasonable. Together they leak a billing credential into a process that was never meant to hold one.
And there was no guardrail on the other side either. Nothing asserted which billing path the CLI was actually on. The one signal we had was that string, "bills the metered API instead of the Max login", and a warning only works if something is reading it.
How to avoid it
If you spawn child processes, treat the environment as an input you must choose, not a default you accept. Build the child's environment from an allowlist of what it needs, rather than copying the parent's and subtracting. Subtraction fails the day someone adds a new secret; an allowlist fails safe.
Know what your runtime loads automatically. If it reads a .env file without being asked, then every secret in that file is in your process environment, whether or not you meant it to be. Keep credentials the pipeline shouldn't use out of the file the pipeline loads.
Make billing path observable. Assert, at startup, which authentication mode a tool is using, and fail loudly if it's the expensive one. A cost control that depends on someone noticing is just a hope with a dashboard.
And watch the anomaly, not the volume. Big numbers invite explanation. The useful question is whether the money should be moving at all.
I'm still a bit annoyed the warning was right there. Anyway.
Every commit, file reference and date above is checked
against this incident's Noctem evidence pack before publication. Evidence:
page-pipeline-billed-metered-api-64m-opus-tokens.