chatboxai/chatbox · error · Error
No refresh token available
Error message
No refresh token available
What it means
Thrown by AnthropicOAuthProvider.refreshToken() when the stored credentials object has no refreshToken. The provider needs a refresh token to POST grant_type=refresh_token to the token endpoint, so an absent one makes refresh impossible and is treated as a hard failure (unlike Qwen/MiniMax which fall back to returning the existing credentials).
Source
Thrown at src/main/oauth/providers/anthropic.ts:138
throw new Error(`Token exchange failed: ${error}`)
}
const data = (await response.json()) as {
access_token: string
refresh_token: string
expires_in: number
}
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresAt: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,
}
},
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/json' },
body: JSON.stringify({
grant_type: 'refresh_token',
client_id: CLIENT_ID,
refresh_token: credentials.refreshToken,
}),
})
if (!response.ok) {
const error = await response.text()
log.error('[OAuth:Anthropic] Token refresh failed:', error)
throw new Error(`Token refresh failed: ${error}`)
}
View on GitHub (pinned to 81571269ad)
Solutions
- Before calling refreshToken, check credentials.refreshToken exists; if absent, trigger a fresh interactive login (startLogin + exchangeCode) instead.
- Verify the persisted credential schema includes refreshToken and that exchangeCode's return value was stored wholesale.
- If you want graceful degradation, treat a missing refreshToken as 're-auth required' and surface a sign-in prompt rather than throwing.
Example fix
// before
const refreshed = await provider.refreshToken(credentials)
// after
if (!credentials.refreshToken) {
// Force a fresh interactive login; refresh is impossible without a token.
return startInteractiveLogin()
}
const refreshed = await provider.refreshToken(credentials) Defensive patterns
Strategy: validation
Validate before calling
function hasRefreshToken(c: { refreshToken?: string | null }): boolean {
return typeof c.refreshToken === 'string' && c.refreshToken.length > 0
}
if (!hasRefreshToken(credentials)) {
// refresh impossible — start a fresh interactive login instead
return startInteractiveLogin()
}
await provider.refreshToken(credentials) Type guard
function hasRefreshToken(c: unknown): c is { refreshToken: string } {
return typeof c === 'object' && c !== null &&
typeof (c as any).refreshToken === 'string' && (c as any).refreshToken.length > 0
} Prevention
- Persist the full credentials object returned by exchangeCode, including refreshToken.
- Run a storage schema check on startup to confirm refreshToken is present before offering refresh.
- Treat a missing refresh token as 're-auth required', not as a recoverable runtime error.
When it happens
Trigger: Calling refreshToken on credentials produced by a flow that never stored a refresh_token; credentials loaded from storage where refreshToken was never persisted; a previous exchange returned a body without refresh_token but exchangeCode still resolved.
Common situations: Storage migration dropped the refreshToken field; an older build saved credentials before refresh support was added; credentials object was constructed manually for testing without a refreshToken.
Related errors
- No refresh token available
- No authorization code found in the input
- Token refresh failed: ${error}
- Token refresh response missing access_token
- Token exchange failed: ${error}
AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12).
Data as JSON: /api/errors/a01350be4f05c646.
Report an issue: GitHub.