chatboxai/chatbox · error · Error

Token refresh failed: ${response.status}

Error message

Token refresh failed: ${response.status}

What it means

Thrown when OpenAI's refresh POST to auth.openai.com/oauth/token returns non-2xx. Note this message interpolates only response.status (not the body), even though the body is read into `text` and logged — the thrown error is less informative than the log. Common cause is an invalid/expired/revoked refresh token.

Source

Thrown at src/main/oauth/providers/openai.ts:105

  async refreshToken(credentials) {
    if (!credentials.refreshToken) {
      throw new Error('No refresh token available')
    }

    const response = await fetch(TOKEN_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'refresh_token',
        refresh_token: credentials.refreshToken,
        client_id: CLIENT_ID,
      }),
    })

    if (!response.ok) {
      const text = await response.text()
      log.error('[OAuth:OpenAI] Token refresh failed:', text)
      throw new Error(`Token refresh failed: ${response.status}`)
    }

    const data = (await response.json()) as {
      access_token?: string
      refresh_token?: string
      expires_in?: number
    }

    if (!data.access_token) {
      throw new Error('Token refresh response missing access_token')
    }

    return {
      accessToken: data.access_token,
      refreshToken: data.refresh_token || credentials.refreshToken,
      expiresAt: typeof data.expires_in === 'number' ? Date.now() + data.expires_in * 1000 - 5 * 60 * 1000 : undefined,
      extra: {
        accountId: extractAccountId(data.access_token),

View on GitHub (pinned to 81571269ad)

Solutions

  1. On 400/401, clear stored credentials and run login() again — the refresh token is dead.
  2. On 5xx or network error, retry once with backoff before surfacing.
  3. Improve the thrown error to include the body text (it is already being read) so callers can branch on the reason.
  4. Serialize refresh calls per account to avoid rotation races.

Example fix

// before
const text = await response.text()
log.error('[OAuth:OpenAI] Token refresh failed:', text)
throw new Error(`Token refresh failed: ${response.status}`)

// after
const text = await response.text()
log.error('[OAuth:OpenAI] Token refresh failed:', response.status, text)
if (response.status >= 500) throw new TransientError('OpenAI refresh transient')
if (response.status === 400 || response.status === 401) {
  throw new ReauthRequiredError(`OpenAI refresh token invalid: ${text}`)
}
throw new Error(`Token refresh failed (${response.status}): ${text}`)
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await provider.refreshToken(credentials)
} catch (e) {
  const msg = String(e)
  if (/Token refresh failed/i.test(msg)) {
  if (/\b(5\d\d|network|fetch)\b/i.test(msg)) return await provider.refreshToken(credentials)
  // 400/401: refresh token invalid — clear and re-login
  await clearStoredCredentials()
  throw new ReauthRequiredError(msg)
  }
  throw e
}

Prevention

When it happens

Trigger: Refresh token revoked by the user at OpenAI; token expired due to inactivity; client_id mismatch; network/5xx failure; concurrent refresh invalidating the prior token.

Common situations: User disconnected the app from their OpenAI account; long idle period; a second device refreshed first; CLIENT_ID constant drifted.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/0bd8d84e22849f96. Report an issue: GitHub.