fix(credential-pool): reduce TTL for HTTP 402 (Payment Required) from 1h to 2min - #74946
fix(credential-pool): reduce TTL for HTTP 402 (Payment Required) from 1h to 2min#74946webtecnica wants to merge 2 commits into
Conversation
|
Good fix! A 1-hour TTL for HTTP 402 is far too punishing — the user recharges credits and the provider stays "exhausted" for an hour with no explanation. 2 minutes gives the balance time to propagate without making the user think the provider is broken. |
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the focused credential-pool fix. The reported behavior is present on current main: agent/credential_pool.py:289-295 sends 402 through the one-hour default, and agent/credential_pool.py:376-383 uses that result when no provider reset time is available.
Problems
- The PR adds no regression test for the new 402 cooldown.
tests/agent/test_credential_pool.py:42-80covers an explicit 429 reset timestamp and:82-145covers 402 sibling-key exhaustion, but neither verifies 402 TTL expiry.
Suggested changes
- Add a focused credential-pool test that creates an exhausted 402 entry without
last_error_reset_atand verifies it remains unavailable before, then becomes selectable after,EXHAUSTED_TTL_402_SECONDS. This exercises the production path inagent/credential_pool.py:376-383and protects the intended distinction from 429.
Automated hermes-sweeper review.
| # 429 (rate-limited), 402 (billing/quota), and other failures cool down after 1 hour. | ||
| # Provider-supplied reset_at timestamps override these defaults. | ||
| EXHAUSTED_TTL_401_SECONDS = 5 * 60 # 5 minutes | ||
| EXHAUSTED_TTL_402_SECONDS = 2 * 60 # 2 minutes — 402 Payment Required |
There was a problem hiding this comment.
Please add a regression test for this status-specific TTL. Existing pool tests cover 402 rotation and 429 reset timestamps, but not a 402 entry becoming available after this new default cooldown.
1f745a5 to
06222a1
Compare
06222a1 to
41ea1fb
Compare
|
Thanks for the review, @teknium1! I've addressed the request and added the regression test for the status-specific 402 TTL. New test: It covers exactly the gap you pointed out — a 402-exhausted entry without a provider-supplied
The test exercises the production fallback path in Branch was also rebased onto the latest upstream Commits:
|
… 1h to 2min When a provider returns HTTP 402 (Payment Required / credits exhausted), the credential pool marks the key as STATUS_EXHAUSTED with the default 1-hour TTL. After the user recharges credits, they must wait the full hour (or longer) for the credential to become available again — even across gateway restarts, since the status is persisted in auth.json. HTTP 402 is fundamentally different from 429 (rate limit): when the user recharges, the key works again immediately. A 2-minute TTL is more appropriate — if credits are still out after 2 min, the next request fails again and re-marks the credential with a fresh TTL. Adds: - EXHAUSTED_TTL_402_SECONDS = 120 (2 minutes) - _exhausted_ttl() now checks for error_code == 402
41ea1fb to
b9e20c1
Compare
|
Rebased the branch onto the latest upstream What changed since the last review:
Why the short 402 TTL matters (production evidence): this has been running in our production deployment since late July. Prepaid/PAYG providers (DeepSeek etc.) return 402 when the balance runs out mid-workload; with the 1-hour default the key stays Related: this behavior is also documented on the configurable-TTL feature request — #33049 (comment) — which proposes moving these cooldowns into @teknium1 — could you re-review? Both points from the sweeper review are addressed (regression test added + mergeable on latest main). |
Problem
When any provider returns HTTP 402 (Payment Required — credits exhausted), the credential pool marks the key as
STATUS_EXHAUSTEDwith the default 1-hour TTL. After the user recharges credits, they must wait the full hour for the credential to become available again — even across gateway restarts, since the status is persisted inauth.json.This affects all API-key providers that use pay-as-you-go billing:
And OAuth providers: anthropic (OAuth), openai-codex, nous, xai-oauth, minimax-oauth, qwen-oauth.
Root Cause
_exhausted_ttl()only had special cases for HTTP 401 (5 min) and 429 (1 hour). HTTP 402 fell through to the default 1-hour TTL, which is appropriate for rate limits but too aggressive for billing/payment errors.Fix
EXHAUSTED_TTL_402_SECONDS = 120(2 minutes)error_code == 402before the 429/generic fallthroughHTTP 402 is fundamentally different from 429: when the user recharges, the key works again immediately. A 2-minute window is enough to prevent rapid retry storms while being short enough that the credential auto-recovers shortly after recharging. If credits are still out after 2 min, the next request fails again and re-marks the credential with a fresh TTL.
Related
Companion PR in Hermes WebUI: nesquena/hermes-webui#6626