mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-19 21:32:20 +03:00
An unrecoverable Claude OAuth refresh (invalid_grant / refresh_token_reused, often a dual-consumer race on the same Claude Max account) left the connection sticky-dead: active, expired and unable to recover without a manual re-auth. #11414 already keeps such a connection retryable for EXPIRED_RETRY_MAX sweeps, but the very same update ran `refreshToken: null` for every rotating provider, Claude included. The next sweep then stops at the `!conn.refreshToken` guard, whose self-heal branch only fires while testStatus is empty or "active" — the row is already "expired", so checkConnection returns silently and the retry budget is never spent. The #11414 regression test missed this because it drives a synthetic provider that is not in ROTATING_REFRESH_PROVIDERS. - Claude opts out of clearing the rotating refresh token on the unrecoverable path, for the same reason #3679 preserves it for non-rotating providers: it is the user's only recovery artifact. Codex and the other rotating providers keep clearing their genuinely consumed one-time-use tokens. - CredentialHealth's sweep now honors the refresh circuit and parks the next attempt on the circuit deadline instead of re-probing a connection whose token refresh is already backing off. - isInRefreshBackoff moves to src/lib/tokenRefreshCircuit.ts so CredentialHealth can use it without importing tokenHealthCheck's auto-starting scheduler; tokenHealthCheck re-exports it for existing callers. Regression test drives the real `claude` provider through two consecutive sweeps: without the fix the token is gone after the first and the second sweep never spends retry 2. A Codex case guards the unchanged behavior. Closes #13183