Repository navigation
Conversation
When every account in a CLIProxyAPI pool is cooling down, the proxy reports the earliest account reset either as "(reset after 1h2m3s)" text or as a model_cooldown body with reset_seconds. The Claude adapter only read the text form, and only directly after a 429's "API Error:" prefix or in 9router's exact wrapped 503 shape, so pool limits failed as provider errors or as usage limits with no reset. Read the reset from either form on a 429, a wrapped gateway 503, or any model_cooldown body, and class a model_cooldown with a reset as a usage limit whatever status carries it. auth_not_found and other errors with no reset stay provider errors. Co-Authored-By: Claude Code <noreply@anthropic.com>
CLIProxyAPI appends the last upstream error to an exhausted pool's 429, e.g. "(reset after 2h54m21s) (last error: GoUsageLimitError (reset after 9h0m0s))". The pool's own reset is the first match. When that first match is "(reset after 0s)", or the pool names no reset, the parser no longer falls back to a reset_seconds quoted inside "(last error: ...)": that is one credential's reset, not the pool's. Adds the proxy-shaped 429 in its raw Claude body and rendered forms, and both last-error fallback cases, to the pool reset table. Co-Authored-By: Claude Code <noreply@anthropic.com>
15fdada to
dc30d01
Compare
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Sol review (9 Nov 2026) — HAPPY on codeReviewed production delta at head
No Sol blocking findings. Delivery note (Opus r2, accepted): production path is the CLIProxyAPI carry-forward half; this PR is hardening for an upstream PR to Review log: Opus r1→author→r2 happy; Sol happy; DeepSeek trial pending on the CLIProxyAPI half (separate artifact). 🤖 Generated with Claude Code |
|
Re-opening to re-run CI against base |
Opus review round 2 (9 Nov 2026, head
|
DeepSeek trial review r1 (9 Nov 2026) — HAPPYRead-only review at head
Verdict: happy — M1 is a real gap in the PR's own rule, worth a follow-up or an in-round fix at Opus's discretion; does not block the documented production path. Opus judging M1–L3 next; judgment to be recorded here before merge. |
Opus judging DeepSeek r1 (9 Nov 2026) — one fix required, then merge
Spec for the fix ( Implementation delegated to Opus now; re-check of its diff, then merge — no new full Opus round needed. |
A gateway's "(last error: …)" quotes one credential's error, not the pool's earliest reset. The "(reset after …)" scan read the whole result while the reset_seconds fallback was cut at the marker, so a hint inside the last error was taken as the pool's reset and a top-level reset_seconds after the message was dropped. Remove each last error up to the end of the JSON string holding it (or the end of rendered text) and run both scans on what remains. Co-Authored-By: Claude Code <noreply@anthropic.com>
Fix re-check (Sol, 9 Nov 2026) — matches Opus's specCommit
No Sol findings. CI run 42591338698 in progress — merge on green (per Opus: no new full round needed after this re-check). |
4614f68
into
base/upstream-main-920ffcdd04-hosted-ci
Problem
When every account in a CLIProxyAPI multi-provider pool (
mimo-v2.6-flash-all-pool, GLM/DeepSeek pools) is exhausted, T3 should enter limited with the real reset of whichever account resets first. The Claude adapter only read a reset from(reset after …)text, and only in two exact places: directly after a 429'sAPI Error:prefix, or in 9router's wrapped 503 shape with the text right after the JSON. CLIProxyAPI'smodel_cooldownbody carries the earliest reset asreset_seconds, which T3 ignored, so pool limits failed as provider errors or as usage limits with no reset.Change
providerFailureFromResultinClaudeAdapterV2.ts:(reset after 1h2m3s)in the result, otherwise"reset_seconds":N(raw or JSON-escaped). Reset = result arrival + seconds. The same bounds as before apply (more than 0 and representable).(reset after …)match, so in… cooling down (reset after 2h54m21s) (last error: GoUsageLimitError (reset after 9h0m0s))T3 uses 2h54m21s, not 9h. If the first match is(reset after 0s), T3 no longer falls back toreset_seconds. Areset_secondsinside(last error: …)is ignored, because it belongs to one credential, not the pool.is_erroror anAPI Error:prefix). The class is stillusage_limit, as before.API Error: 503 [x] [429]: {"type":"error","error":{"type":"rate_limit_error",…): the reset can now be inside the JSON message (where the CLIProxyAPI patch puts it) or come fromreset_seconds."code":"model_cooldown"on any status, with a reset:usage_limitwithresetAt, retryable.auth_not_found/no auth candidates, and anything else with no reset signal, staysprovider_errorwith no reset. A 401 oroverloaded_errorwrap still staysprovider_error.Tests
ClaudeAdapterV2.test.tsadds 16 cases ("reads a credential pool's earliest reset from …"):(reset after 2h54m21s), a reset outside theAPI Error:prefix,model_cooldownwithreset_seconds(raw and JSON-escaped), and a Clauderate_limit_errorwithreset_seconds.model_cooldown, and wrapped 503s with the reset in the message or inreset_seconds.reset_seconds, andauth_not_found(plain text and JSON), which stayprovider_error.API Error: 429 {…rate_limit_error…}form and the renderedAPI Error: Request rejected (429) · …form. Both give 2h54m21s.(reset after 0s)with areset_secondsin the last error gives no reset. Amodel_cooldownwhose onlyreset_secondsis in its last error staysprovider_error. Both fail without the r1 parser change.The existing gateway, direct-429 and buffered-wake cases are unchanged and still pass.
vp test run src/orchestration-v2/Adapters/ClaudeAdapterV2.test.ts: 361 passed, 1 skipped (withAgentContextBudget.test.ts: 363 passed)vp run check:changed: format, lint, knip, graphql and typecheck pass. Its root test step fails on Windows before running any test: the web dependency optimizer cannot load a path containingAsus%20X13. This is a local path problem, unrelated to this change.ClaudeReplayFixtures.integration.test.tshas 2 Bash-streaming failures that also happen on unmodifiedupstream/mainon this machine. Everything else passes.Base
This targets
base/upstream-main-920ffcdd04-hosted-ci, which ispingdotgg/t3codemainat 920ffcd plus one CI-only commit (b5d29a8). That commit ports the fork's hosted-runner fallback from #2 toci.ymlandmobile-fingerprint-check.yml:github.repository_owner == 'pingdotgg' && 'blacksmith-…' || 'ubuntu-24.04'(ormacos-26). The earlier base,base/upstream-main-920ffcdd04, requested only Blacksmith runners, which the fork doesn't have, so CI stayed queued forever. The fork'smainpredates this adapter code. The PR diff is still only the two adapter files.CI on hosted runners
Run 42550208059 ran on GitHub-hosted
ubuntu-24.04/macos-26runners.ClaudeAdapterV2.test.tspassed on Test Server 5 (362 tests). Lint, Build and Typecheck, Test, Rust, Release Smoke, Mobile Native Static Analysis, Test Server 2–8 and Native fingerprint diff also passed.Checkfailed because of two problems this PR doesn't touch:storageCleanup.test.ts > compares the checked-out branch without a full statusgetsoutcome: "failed"("gitfile does not point to a valid repository"), not the"kept"it expects. The runner has git 2.56.0. This is upstream snapshot code.Review log
(reset after 0s)no longer falls back toreset_seconds, and areset_secondsinside(last error: …)is ignored. Two rows cover this.🤖 Generated with Claude Code