Skip to content

fix(routing): treat 400 insufficient-credits responses as billing failures - #7

Merged
yelixir-dev merged 1 commit into
yelixir-dev:mainfrom
A8Cl233395:fix/400-insufficient-credts
Oct 3, 2026
Merged

yelixir-dev merged 1 commit into
yelixir-dev:mainfrom
A8Cl233395:fix/400-insufficient-credts

Conversation

@A8Cl233395

@A8Cl233395 A8Cl233395 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Problem

CommandCode reports a depleted key as a bare HTTP 400 whose body says "insufficient credits" — not as 402. The credential router treated bare 400s as client-scoped errors: no cooldown, the key stayed in rotation, and every request on that key kept failing even though the failure was entirely credential-scoped.

Changes

  • Add isInsufficientCreditsMessage() — matches "insufficient credits / balance" in the upstream error text.
  • Add upstreamErrorMessage() — extracts the error message from an HTTP JSON body (error.message / message) or an SSE error event, so both tunnels can classify failures by body content, not just status code.
  • isFatalCredFailure() now takes the error message: a 400 with an insufficient-credits body is classified as credential-scoped.
  • recordFailure() starts an insufficient_credits cooldown of max(cooldownMs, billingRefreshMs) for such responses. This cooldown survives billing refreshes: upstream pre-charges the estimated request cost, so a positive balance can still be too small to serve, and billing snapshots cannot see that threshold. It expires on its own.
  • Both the alpha tunnel (commandcode.ts) and the provider tunnel (provider.ts) pass the upstream message into recordFailure.

A plain 400 ("Model not allowed for this plan.") remains client-scoped: no cooldown, no behavior change. 402 handling is unchanged and still clears automatically once billing reports available credits.

Tests

  • Router: a 400 with insufficient credits starts an insufficient_credits cooldown; the cooldown survives a forced billing refresh; a plain 400 (with or without a body) does not cool the key down; 402 still clears when billing recovers.
  • Client: a 400 insufficient-credits response cools the credential down; a plain 400 does not.

Note

This PR only classifies and cools the key down — the request itself still fails on the first attempt. Key rotation for credential-scoped failures is added in the follow-up retry PR (stacked on this one).

@A8Cl233395
A8Cl233395 force-pushed the fix/400-insufficient-credts branch from cee48c8 to 0a6abfd Compare October 2, 2026 15:05
…lures

Upstream reports an empty balance as HTTP 400 whose body says 'You have insufficient credits to make this request' instead of the 402 that recordFailure and isFatalCredFailure expect. A bare 400 stays provider-scoped and released without marking, but the same status with an insufficient-credits body is now classified as credential-scoped billing failure, so the key cools down like a 402 instead of staying ready and failing every request. Failure plumbing carries the upstream error text from HTTP responses, stream error events, and caught CommandCodeHttpErrors in both the CommandCode and provider clients. Cover with router and client regression cases for the billing cooldown and the untouched plain-400 path.
@yelixir-dev

Copy link
Copy Markdown
Owner

Thanks for this — classifying the 400 "insufficient credits" response as credential-scoped matches what the CLI itself does (CLI 1.74.0 checks 400 plus a message containing "insufficient credits"), so a drained key no longer stays in rotation.

Verified before merging: merged cleanly onto current main; tsc --noEmit, eslint, Prettier and the full vitest suite (16 files, 299 tests) pass on Linux, and CI is green on Node 20/22/24. Merging now, then #8 on top.

@yelixir-dev
yelixir-dev merged commit 278e83a into yelixir-dev:main Oct 3, 2026
3 checks passed
yelixir-dev added a commit that referenced this pull request Oct 3, 2026
Release PR #7 and PR #8: a 400 "insufficient credits" response is
now credential-scoped (cooldown that survives billing refreshes,
matching the CLI's own check), and insufficient-credits 400s and
403s rotate the request to another key on both the Alpha and
Provider paths. Plain 400/404/422 still fail fast.

Update the README routing paragraph (en/ko/zh), KNOW_HOW,
ARCHITECTURE and both deployment guides, and record the release in
PROCESS_LOG.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants