← All incidents

majormonitoring-gapverifiedfirsthand

An out-of-credit error came back with exit 0, so empty batch runs looked like success

In a batch loop built on `claude -p`, running out of API credit printed "Credit balance is too low" to stdout and exited 0, so empty results piled up looking like successful runs.

Observed
Severity score
5/10
Blast radius
uptime, financial
Tags
#claude-code#batch#exit-code#billing#silent-failure

Cause

The CLI was logged in to an API-billing organization rather than the subscription, and that organization's credit had run dry. On top of that, the CLI didn't treat the failure as an error; it returned it as normal output.

Consequence

Every claude -p call failed, and the batch had no way to tell from exit codes alone. Later, seeing the same message, the operator topped up the wrong billing account and put $5.50 where it couldn't help.

Fix

Re-running /login and choosing the subscription account fixed it, with no credit purchase needed. Batches no longer trust exit codes: every output is grepped for Credit balance is too low.

What happened

Repetitive work that needed an LLM judgment call per item was running as a claude -p loop: call Claude Code non-interactively once per item, take the result, move on. A very ordinary batch.

At some point, every claude -p call started failing. The batch didn’t stop. The failure message, “Credit balance is too low,” went to stdout, and the exit code was 0. From the loop’s point of view, each call finished cleanly and returned something.

The chaos on the ground

Checking exit codes was completely useless here. Success and failure both exited 0 and both printed a string to stdout. The only difference was the content.

Digging in, the real cause sat one step before the balance itself. The CLI’s saved login pointed at an API-billing organization, not the subscription. That organization was out of credit and couldn’t receive overage credit either. Stripping environment variables didn’t help, because the problem was the stored login itself.

It didn’t end there. Later, an API-key call returned the same “Credit balance is too low.” Same words, so presumably the same wallet — and the operator topped up $5.50 on the subscription billing page. But API keys bill through a separate account, and that money did nothing for the API. The same message was pointing at two different wallets.

Root cause

  • The CLI was logged in to an API-billing organization whose credit was gone.
  • claude -p reports running out of credit not as an error but with exit 0 and a line on stdout, so an empty run was recorded in exactly the same shape as a successful one.
  • The message “Credit balance is too low” appears for both the CLI/app path and the API-key path. They bill from different places, and the wording gives no hint which one you’re looking at.

The fix

  • Started claude, ran /login, and picked the subscription account. That fixed it; no credit purchase was needed.
  • Batches no longer trust exit codes. Each output’s content is checked, and grepping for Credit balance is too low is now mandatory.
  • While credit is out, work can be routed to subagents that run on the subscription side and need no API credit.
  • When an API key returns the same message, check the balance in the API console rather than the app’s billing page, and make sure the right organization is selected. Same message does not mean same wallet.

Sources

  • Firsthand account from the site operator. There is no public write-up to link to.