Skip to content

fix(aux): discard stale credentials after auth refresh - #72263

Closed
marveon wants to merge 1 commit into
NousResearch:mainfrom
marveon:fix/aux-anthropic-401-stale-token-retry
Closed

fix(aux): discard stale credentials after auth refresh#72263
marveon wants to merge 1 commit into
NousResearch:mainfrom
marveon:fix/aux-anthropic-401-stale-token-retry

Conversation

@marveon

@marveon marveon commented Jul 26, 2026

Copy link
Copy Markdown

Summary

  • call_llm() / async_call_llm() correctly detect a 401 on an auxiliary request (context compression, title generation, ...), call _refresh_provider_credentials("anthropic") to rotate the Claude Code OAuth token on disk, and evict the cached client — but then retry using the exact resolved_api_key string that had just been rejected.
  • _get_cached_client() treats an explicit api_key as authoritative and never re-derives it, so the "retry" silently rebuilds a client with the same dead token and 401s again.
  • Observed in production: an auxiliary client kept hitting OAuth access token has been revoked every ~5-9 minutes for 4+ hours while the main conversation loop (which resolves credentials independently via credential_pool) kept working fine. Because context compression could never succeed, the session transcript grew unchecked to ~336k tokens, and the turn eventually died once the account also hit a real billing/usage cap and fell back to a local model with a much smaller context window.
  • Fix: re-resolve the token via resolve_anthropic_token() before retrying, in both the sync and async auth-refresh-retry paths.

Test plan

  • Added test_call_llm_anthropic_401_retry_does_not_reuse_stale_api_key, verified it fails against the pre-fix code (stashed the fix and re-ran) and passes with the fix.
  • pytest tests/agent/test_auxiliary_client.py — 363 passed.
  • pytest tests/agent/test_auxiliary_client_xai_oauth_recovery.py — all passed (unaffected provider paths).

@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/anthropic Anthropic native Messages API area/auth Authentication, OAuth, credential pools sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data P2 Medium — degraded but workaround exists duplicate This issue or pull request already exists labels Jul 26, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #62008: its current generic retry path already clears the stale request-scoped API key after a successful auxiliary auth refresh, including the Anthropic case covered here.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused reproduction. The stale-key premise is confirmed on current main: the sync retry forwards resolved_api_key at agent/auxiliary_client.py:8379, and the async retry does the same at agent/auxiliary_client.py:9020; _get_cached_client() preserves a non-empty explicit key (agent/auxiliary_client.py:6707-6721).

Problems

  • The patch special-cases Anthropic, but both paths use the same generic retry helpers for every refreshable provider. Other providers can still rebuild with the rejected request-scoped key. Open PR fix(auxiliary): discard stale credentials after auth refresh #62008 contains the broader stale-input-clearing direction, but it is not yet on main.
  • The new test covers only the sync Anthropic path; the async Anthropic hunk has no corresponding regression assertion.

Suggested changes

  • Consolidate this into a provider-neutral stale-input reset after successful auth refresh, before the retry helper is called.
  • Add an async Anthropic 401 test asserting that the retry uses the refreshed credential.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 30, 2026
Auxiliary auth-refresh retries could replay request-scoped or main-runtime credentials after the provider store was refreshed. Rebuild retry clients from the refreshed auth store instead of passing the token that just received a 401.

Clear stale retry inputs for refreshable providers in both sync and async paths, including Anthropic and Codex-style auto routes, and cover the stale-input behavior with sync/async regression assertions.
@marveon
marveon force-pushed the fix/aux-anthropic-401-stale-token-retry branch from a0c9968 to 0234c18 Compare July 31, 2026 19:18
@marveon marveon changed the title fix(aux): stop retrying Anthropic auth-refresh with the stale api_key fix(aux): discard stale credentials after auth refresh Jul 31, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Twenty-two PRs address or reference this complex, but their diffs cover distinct causes behind similar Anthropic 400/429 symptoms: billing classification, endpoint and credential selection, OAuth tool/system-prompt fingerprints, billing attribution, subprocess alternatives, and stale credential reuse. For target #72263, the relevant cause is narrower: auxiliary retries retain a rejected request-scoped credential after a successful auth refresh.

Related pull requests

Duplicates

#6498, #21019, #40020, and #40073 overlap on billing classification and guidance; #17681, #28872, and #46687 form the MCP-prefix chain. #48177, #48202, and #69844 share the billing-marker mechanism; #76669 was salvaged into #76807; and #72263 overlaps the broader stale-input reset in #62008.

Suggested consolidation

Close #72263 as duplicate of #62008, with the explicit chain #72263#62008: despite the keep_open review on #72263, its diff clears the rejected resolved_api_key and stale runtime at the generic auxiliary retry call sites, while the contributor discussion identifies #62008 as the provider-neutral implementation of that same class. Preserve or transfer #72263's sync and async Anthropic regression assertions to #62008; keep #75697 and #76807 open separately because they address distinct credential-identity and OAuth wire-schema causes.

Cross-PR triage: Reviewed 22 pull requests and 22 issues in this complex. Each diff was read against this issue; Assessment working set: 244 kB of PR diffs, 253 kB of issue/PR text, 82 kB of discussion (169 comments), 110 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants