fix(agent): consult credential pool in _restore_primary_runtime to avoid stale snapshot key - #25730
dusterbloom wants to merge 1 commit into
Conversation
…oid stale snapshot key (NousResearch#25205) _restore_primary_runtime() blindly restores api_key from the init-time snapshot, bypassing any credential pool rotation that occurred during the previous turn. After a 401/429/402 triggers pool rotation, the next turn restores the exhausted key, immediately fails again, and cascades into unnecessary fallback. Now both _restore_primary_runtime and _try_recover_primary_transport consult pool.current() / pool.select() after restoring snapshot state. When a pool entry is available, _swap_credential applies it (rebuilding the client with the fresh key). Falls back to snapshot behavior when no pool exists or pool access fails. Closes NousResearch#25205
|
Competing fix: PR #25206 also targets the same Related: #15434 (same function, cooldown check) and #25277 (pool in |
|
Closing as stale: branch has fallen behind main and is conflicting, with no active review. Will reopen with a clean rebase if the change is still wanted. |
|
Closing as a duplicate of #25206 (by @jmmaloney4, submitted first, who also reported the underlying issue #25205). Both PRs fixed the same bug — Merged via #53913 (commit f0de4c6 on |
Summary
Fixes #25205
_restore_primary_runtime()blindly restoresapi_keyfrom the init-time snapshot, bypassing any credential pool rotation that occurred during the previous turn. After a 401/429/402 triggers pool rotation, the next turn restores the exhausted key, immediately fails again, and cascades into unnecessary cross-provider fallback.Root Cause
resolve_runtime_provider()selects credential A → snapshotted in_primary_runtime["api_key"]_swap_credential()updatesself.api_keyto credential B → but_primary_runtimeis never updated_restore_primary_runtime()restores stale credential A from snapshot → immediate 401 againThe same bug exists in
_try_recover_primary_transport().Fix
Both
_restore_primary_runtime()and_try_recover_primary_transport()now consult the credential pool after restoring snapshot state:pool.current()is tried first (same entry used last turn)pool.select()is the fallback (picks best available)Testing
test_primary_runtime_restore.pytests passtest_credential_pool.pytests passTestCredentialPoolBypass:test_restore_uses_pool_current_entry— verifies pool entry used instead of snapshottest_restore_falls_back_to_snapshot_when_no_pool— no-pool backward compattest_restore_handles_pool_exception_gracefully— pool error → snapshot fallbacktest_restore_prefers_current_over_select—current()called first