You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Anthropic API key's credit balance — verified in the error log, 2026-08-12 06:59:49 and 07:07:09, repeat_count: 2, instance f8ddc272 (Heartfull):
Anthropic (400): Your credit balance is too low to access the Anthropic API. at runAnthropic … chatComplete … runAgentThink … AgentDO.runTurn
The Usage page cannot separate them, because 99.62% of the account's notional value sits in the unknown payer bucket — and it does not have to. The platform already has the mechanism to resolve it, it is one stored token, and no surface says so.
Measured — the whole causal chain, end to end
No Claude Code sign-in token is stored.GET /v1/keys/status, production, 2026-08-13:
Every Repo Coder preset runs auto.lib/coding-engines.ts:50-56, resolveEngineEnv:
// "subscription" | "auto" — the stored `claude setup-token`.consttoken=awaitgetUserProviderKey(env,uid,"claude-code");// No stored setup-token → fall through to the machine's OWN login …if(!token)returnstripProviderKey;// → { ANTHROPIC_API_KEY: "" }
The runner deletes on empty, so the merged env has neither var. packages/browser-runner/src/coding/headless.ts:15-25:
if(v==="")deleteout[k];
The runner therefore reports machine-login.packages/browser-runner/src/coding/engine-auth.ts:63-67 — neither ANTHROPIC_API_KEY nor CLAUDE_CODE_OAUTH_TOKEN present → return "machine-login".
machine-login maps to NULL, deliberately.lib/usage-payer.ts:78-82:
NULL is excluded from every money figure.lib/usage.ts:369 (isCharged(null) === false) and CHARGED_SQL (usage-payer.ts:62).
What that costs, in numbers
usage_summary, range: "all", 2026-08-13:
byPayer unknown $9,584.87 3,462 calls 99.62% of notional value
byok-api $36.34 1,866 calls
platform $0.02 393 calls
subscription (no bucket — ZERO rows have ever resolved to it)
byKind engine $9,541.31 449 calls ← 99.5% of the unknown bucket
Cross-check that the engine rows really are the unknown bucket: unknown.outputTokens (3,603,049) − engine.outputTokens (3,283,599) = 319,450, and 319,450 + byok-api (232,914) + platform (16,202) = 568,566 = exactly totals.outputTokens − engine.outputTokens. The identity closes to the token, on both the output and the cache-read column. All 449 engine rows are payer-unknown.
Why this is a bug and not just an honest NULL
The NULL itself is correct and #92's refusal to guess is right. What is missing is that the platform knows how to make it knowable and never tells the owner. Every one of these is already true today:
resolveEngineEnv will inject CLAUDE_CODE_OAUTH_TOKEN the moment a claude-code key exists (coding-engines.ts:58-61) — for both auto and subscription modes.
That flips step 4 to "subscription", step 5 to payer: "subscription", and the Usage page's largest row from "Payer not established" to "Drawn from a subscription" with the existing note (usage-payer.ts:PAYER_NOTE): "No per-token charge; it draws your plan's allowance, which is measured in tokens over a rolling window."
That is exactly the sentence this owner needs, because it is the sentence that separates his $36 API bill from his subscription's session limit.
Choosing api-key mode instead resolves to byok-api and folds the engine into the charged total — the other correct answer, for someone who genuinely is paying per token.
Today the console's ⚙ CLI engines panel offers all four modes with no indication that three of them leave the platform unable to attribute the resulting spend, and GET /v1/keys/status shows claude-code as just one more provider row among thirteen.
What to do, cheapest first
Say it where the ambiguity is created. The Usage page's unknown payer row already carries a good note. Add the action: "449 calls ($9,584.87 of value) ran on a login stored on your machine, so we cannot tell whether they were billed. Store a claude setup-token under Claude Code, or set the engine's sign-in to API key, and this becomes attributable." Link to the engines panel.
Say it where the mode is chosen.EnginesModal — annotate each sign-in mode with what it means for attribution: api-key → "charged spend, visible on Usage"; subscription → "no per-token charge, tokens only"; auto / machine → "attribution unknown unless a token is stored".
Make the resolved payer visible in aggregate, not only per session.Show which credential a coding session actually used (subscription vs per-token API key) and what the engine really is #248 shipped engineAuthReport (coding-engines.ts:213-224) so one session's credential is legible on the Coding tab. Nothing rolls that up. A count of sessions by resolved credential, on the Usage page beside the payer breakdown, is what makes "all of them are unattributed" noticeable rather than something you have to click nine sessions to discover.
Alternatives considered and rejected
Map machine-login to subscription. Rejected, emphatically. usage-payer.ts:70-77 argues it out already: it is the most common resolution, and it may be a plan OR an API key configured inside the CLI. Guessing here is the exact error Triggers: let a cron/webhook trigger run a durable pipeline, not just the 4 canned actions #92 exists to remove, and it would silently zero the charged figure for someone genuinely paying per token.
Ask Claude Code what it authenticated with. No such field on the stream-json result event. total_cost_usd is computed at list rates for subscribers too (Anthropic's own docs, cited at usage.ts:150-156), so its presence proves nothing.
Read ~/.claude/.credentials.json on the runner to tell a stored OAuth login from a stored API key. Genuinely more informative than the env probe and would resolve the auto case without any user action — but it means the runner reading a credential file to classify it, which is a materially larger trust step than the presence-only env check engine-auth.ts is careful to stay inside. Open question for the owner; I would ship 1–3 first, since they cost nothing and may make it unnecessary.
Acceptance criteria
Storing a claude-code key and starting a session produces ai_usage rows with payer = 'subscription'; the Usage page shows a "Drawn from a subscription" bucket.
The unknown payer row states the cause and the remedy, with the call count and value.
The engines panel states the attribution consequence of each sign-in mode.
A test asserts the auto + no-stored-token path yields machine-login → NULL (locking in that this is a disclosed gap, not a silent one).
Regression risk
Storing a setup-token changes real behaviour, not just reporting: resolveEngineEnv will then inject CLAUDE_CODE_OAUTH_TOKENand strip ANTHROPIC_API_KEY on every auto session. For a machine whose shell exports a working key and whose stored setup-token is stale, that turns a working engine into a failing one. Any prompt to store a token must be paired with the existing engineAuthWarning path, and the docs (platform-docs/coder-multi-machine.md and docs/coding-engines.md) should state the swap.
Verified: the key status, the six code hops, the payer arithmetic, and the credit-balance errors — all from production reads and source. Inferred: that his engine sessions are in fact running on a Claude subscription (from the "session limit" reports); the platform cannot confirm it, which is the whole point of this issue.
Related: #248 (per-session credential display), #346 / migration 0092 (the payer axis), #356 (a drop of this same observation), #485 (which ceiling to enforce), #543, #544.
Two credentials, two independent limits, one bucket labelled "unknown"
This account is hitting two different ceilings on two different Anthropic credentials:
repeat_count: 2, instancef8ddc272(Heartfull):The Usage page cannot separate them, because 99.62% of the account's notional value sits in the
unknownpayer bucket — and it does not have to. The platform already has the mechanism to resolve it, it is one stored token, and no surface says so.Measured — the whole causal chain, end to end
GET /v1/keys/status, production, 2026-08-13:{ "id": "claude-code", "name": "Claude Code (Coder engine sign-in)", "hasKey": false, "createdAt": null }auto.lib/coding-engines.ts:50-56,resolveEngineEnv:packages/browser-runner/src/coding/headless.ts:15-25:machine-login.packages/browser-runner/src/coding/engine-auth.ts:63-67— neitherANTHROPIC_API_KEYnorCLAUDE_CODE_OAUTH_TOKENpresent →return "machine-login".machine-loginmaps to NULL, deliberately.lib/usage-payer.ts:78-82:lib/usage.ts:369(isCharged(null) === false) andCHARGED_SQL(usage-payer.ts:62).What that costs, in numbers
usage_summary,range: "all", 2026-08-13:Cross-check that the engine rows really are the unknown bucket:
unknown.outputTokens (3,603,049) − engine.outputTokens (3,283,599) = 319,450, and319,450 + byok-api (232,914) + platform (16,202) = 568,566= exactlytotals.outputTokens − engine.outputTokens. The identity closes to the token, on both the output and the cache-read column. All 449 engine rows are payer-unknown.Why this is a bug and not just an honest NULL
The NULL itself is correct and #92's refusal to guess is right. What is missing is that the platform knows how to make it knowable and never tells the owner. Every one of these is already true today:
resolveEngineEnvwill injectCLAUDE_CODE_OAUTH_TOKENthe moment aclaude-codekey exists (coding-engines.ts:58-61) — for bothautoandsubscriptionmodes."subscription", step 5 topayer: "subscription", and the Usage page's largest row from "Payer not established" to "Drawn from a subscription" with the existing note (usage-payer.ts:PAYER_NOTE): "No per-token charge; it draws your plan's allowance, which is measured in tokens over a rolling window."api-keymode instead resolves tobyok-apiand folds the engine into the charged total — the other correct answer, for someone who genuinely is paying per token.Today the console's ⚙ CLI engines panel offers all four modes with no indication that three of them leave the platform unable to attribute the resulting spend, and
GET /v1/keys/statusshowsclaude-codeas just one more provider row among thirteen.What to do, cheapest first
unknownpayer row already carries a good note. Add the action: "449 calls ($9,584.87 of value) ran on a login stored on your machine, so we cannot tell whether they were billed. Store aclaude setup-tokenunder Claude Code, or set the engine's sign-in to API key, and this becomes attributable." Link to the engines panel.EnginesModal— annotate each sign-in mode with what it means for attribution:api-key→ "charged spend, visible on Usage";subscription→ "no per-token charge, tokens only";auto/machine→ "attribution unknown unless a token is stored".engineAuthReport(coding-engines.ts:213-224) so one session's credential is legible on the Coding tab. Nothing rolls that up. A count of sessions by resolved credential, on the Usage page beside the payer breakdown, is what makes "all of them are unattributed" noticeable rather than something you have to click nine sessions to discover.Alternatives considered and rejected
machine-logintosubscription. Rejected, emphatically.usage-payer.ts:70-77argues it out already: it is the most common resolution, and it may be a plan OR an API key configured inside the CLI. Guessing here is the exact error Triggers: let a cron/webhook trigger run a durable pipeline, not just the 4 canned actions #92 exists to remove, and it would silently zero the charged figure for someone genuinely paying per token.resultevent.total_cost_usdis computed at list rates for subscribers too (Anthropic's own docs, cited atusage.ts:150-156), so its presence proves nothing.~/.claude/.credentials.jsonon the runner to tell a stored OAuth login from a stored API key. Genuinely more informative than the env probe and would resolve theautocase without any user action — but it means the runner reading a credential file to classify it, which is a materially larger trust step than the presence-only env checkengine-auth.tsis careful to stay inside. Open question for the owner; I would ship 1–3 first, since they cost nothing and may make it unnecessary.Acceptance criteria
claude-codekey and starting a session producesai_usagerows withpayer = 'subscription'; the Usage page shows a "Drawn from a subscription" bucket.unknownpayer row states the cause and the remedy, with the call count and value.auto+ no-stored-token path yieldsmachine-login→ NULL (locking in that this is a disclosed gap, not a silent one).Regression risk
Storing a setup-token changes real behaviour, not just reporting:
resolveEngineEnvwill then injectCLAUDE_CODE_OAUTH_TOKENand stripANTHROPIC_API_KEYon everyautosession. For a machine whose shell exports a working key and whose stored setup-token is stale, that turns a working engine into a failing one. Any prompt to store a token must be paired with the existingengineAuthWarningpath, and the docs (platform-docs/coder-multi-machine.mdanddocs/coding-engines.md) should state the swap.Verified: the key status, the six code hops, the payer arithmetic, and the credit-balance errors — all from production reads and source. Inferred: that his engine sessions are in fact running on a Claude subscription (from the "session limit" reports); the platform cannot confirm it, which is the whole point of this issue.
Related: #248 (per-session credential display), #346 / migration 0092 (the payer axis), #356 (a drop of this same observation), #485 (which ceiling to enforce), #543, #544.