Repository navigation
fix(routing): treat 400 insufficient-credits responses as billing failures - #7
Merged
yelixir-dev merged 1 commit intoOct 3, 2026
Conversation
A8Cl233395
force-pushed
the
fix/400-insufficient-credts
branch
from
October 2, 2026 15:05
cee48c8 to
0a6abfd
Compare
…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.
A8Cl233395
force-pushed
the
fix/400-insufficient-credts
branch
from
October 2, 2026 15:24
0a6abfd to
187107e
Compare
Owner
|
Thanks for this — classifying the 400 "insufficient credits" response as credential-scoped matches what the CLI itself does (CLI 1.74.0 checks Verified before merging: merged cleanly onto current |
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
isInsufficientCreditsMessage()— matches "insufficient credits / balance" in the upstream error text.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 aninsufficient_creditscooldown ofmax(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.commandcode.ts) and the provider tunnel (provider.ts) pass the upstream message intorecordFailure.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
insufficient_creditscooldown; 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.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).