Skip to content

fix: expand recoverable error codes in token refresh handler#73

Open
Zeeeen0 wants to merge 1 commit intotickernelz:masterfrom
Zeeeen0:fix/expand-recoverable-error-codes
Open

fix: expand recoverable error codes in token refresh handler#73
Zeeeen0 wants to merge 1 commit intotickernelz:masterfrom
Zeeeen0:fix/expand-recoverable-error-codes

Conversation

@Zeeeen0
Copy link
Copy Markdown

@Zeeeen0 Zeeeen0 commented Mar 19, 2026

Problem

When refreshAccessToken() fails with OIDC standard error codes like invalid_client, invalid_grant, or unauthorized_client, the handleRefreshError() method does not recognize them as recoverable. This causes an immediate throw that exits the request loop entirely, instead of marking the account as unhealthy and allowing account rotation or retry.

Observed error log:

2026-03-19T12:08:57.537Z ERROR: Token refresh failed {"email":"xxx@gmail.com","code":"invalid_client","message":"Refresh failed: Client is expired"}
2026-03-19T12:08:57.867Z ERROR: Token refresh unrecoverable {"email":"xxx@gmail.com","code":"invalid_client","message":"Refresh failed: Client is expired"}

The invalid_client code is not in the recoverable list, so it falls through to an unrecoverable throw — even though the error message clearly indicates an expired/auth-related issue that should be handled gracefully.
This can manifest as:

  • Instant request failure with no recovery attempt
  • "Exceeded max iterations (20)" loops when combined with other retry paths

Root Cause

The recoverable error list in handleRefreshError() only included:

  • ExpiredTokenException
  • InvalidTokenException
  • HTTP_401
  • HTTP_403
  • Invalid refresh token provided (message match)
    Any error code outside this list falls through to an unrecoverable throw, even when the error is clearly auth-related and should trigger unhealthy marking + account rotation.

Fix

Expand the recoverable error list with:

  • invalid_client — OIDC standard, returned when client credentials are rejected or expired
  • invalid_grant — OIDC standard, returned when refresh token is expired/revoked
  • unauthorized_client — OIDC standard, returned when client lacks permission
  • error.message.toLowerCase().includes('expired') — catch-all for any expiry-related message
  • error.code?.toLowerCase().includes('expired') — catch-all for any expiry-related error code
    These errors now correctly trigger markUnhealthy()shouldContinue: true, allowing the request loop to attempt account rotation or kiro-cli sync recovery.

Changes

  • src/core/auth/token-refresher.ts: Added 5 new conditions to the recoverable error check in handleRefreshError()

Add OIDC standard error codes (invalid_client, invalid_grant,
unauthorized_client) and expired keyword matching to the recoverable
error list in handleRefreshError. Previously these errors caused an
immediate throw, bypassing unhealthy marking and account rotation,
which could lead to max iteration loops or instant request failure.
@evlic
Copy link
Copy Markdown

evlic commented Mar 23, 2026

Can this PR fix the issue of the opencode process being interrupted?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants