From b408379e9d44bae4fb366d19183840dd52c39a16 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 9 Apr 2026 02:37:23 -0700 Subject: [PATCH] fix: reduce credential exhaustion TTL from 24 hours to 1 hour (#6504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 24-hour default cooldown for 402-exhausted credentials was far too aggressive — if a user tops up credits or the 402 was caused by an oversized max_tokens request rather than true billing exhaustion, they shouldn't have to wait a full day. Reduce to 1 hour (matching the existing 429 TTL). Inspired by PR #6493 (michalkomar). --- agent/credential_pool.py | 6 ++--- tests/agent/test_credential_pool.py | 36 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/agent/credential_pool.py b/agent/credential_pool.py index a47901c847..dd2c9abc5e 100644 --- a/agent/credential_pool.py +++ b/agent/credential_pool.py @@ -64,10 +64,10 @@ SUPPORTED_POOL_STRATEGIES = { } # Cooldown before retrying an exhausted credential. -# 429 (rate-limited) cools down faster since quotas reset frequently. -# 402 (billing/quota) and other codes use a longer default. +# 429 (rate-limited) and 402 (billing/quota) both cool down after 1 hour. +# Provider-supplied reset_at timestamps override these defaults. EXHAUSTED_TTL_429_SECONDS = 60 * 60 # 1 hour -EXHAUSTED_TTL_DEFAULT_SECONDS = 24 * 60 * 60 # 24 hours +EXHAUSTED_TTL_DEFAULT_SECONDS = 60 * 60 # 1 hour # Pool key prefix for custom OpenAI-compatible endpoints. # Custom endpoints all share provider='custom' but are keyed by their diff --git a/tests/agent/test_credential_pool.py b/tests/agent/test_credential_pool.py index 891ab68a82..c3bde95156 100644 --- a/tests/agent/test_credential_pool.py +++ b/tests/agent/test_credential_pool.py @@ -214,6 +214,42 @@ def test_exhausted_entry_resets_after_ttl(tmp_path, monkeypatch): assert entry.last_status == "ok" +def test_exhausted_402_entry_resets_after_one_hour(tmp_path, monkeypatch): + """402-exhausted credentials recover after 1 hour, not 24.""" + monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes")) + _write_auth_store( + tmp_path, + { + "version": 1, + "credential_pool": { + "openrouter": [ + { + "id": "cred-1", + "label": "primary", + "auth_type": "api_key", + "priority": 0, + "source": "manual", + "access_token": "***", + "base_url": "https://openrouter.ai/api/v1", + "last_status": "exhausted", + "last_status_at": time.time() - 3700, # ~1h2m ago + "last_error_code": 402, + } + ] + }, + }, + ) + + from agent.credential_pool import load_pool + + pool = load_pool("openrouter") + entry = pool.select() + + assert entry is not None + assert entry.id == "cred-1" + assert entry.last_status == "ok" + + def test_explicit_reset_timestamp_overrides_default_429_ttl(tmp_path, monkeypatch): monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes")) _write_auth_store(