router-for-me/CLIProxyAPI · error
xai token refresh: refresh token is required
Error message
xai token refresh: refresh token is required
What it means
RefreshTokens was called with an empty or whitespace-only refresh token. The client validates inputs before any network call because a refresh without a credential is a caller-side contract violation, not a server condition.
Source
Thrown at internal/auth/xai/xai.go:333
return nil, fmt.Errorf("xai device token error: %s", payload.Error), interval, false
}
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("xai device token request failed with status %d: %s", resp.StatusCode, strings.TrimSpace(string(body))), interval, false
}
if strings.TrimSpace(payload.AccessToken) == "" {
return nil, fmt.Errorf("xai device token response missing access_token"), interval, false
}
email, subject := parseJWTIdentity(payload.IDToken)
return buildTokenData(payload.AccessToken, payload.RefreshToken, payload.IDToken, payload.TokenType, payload.ExpiresIn, email, subject), nil, interval, false
}
// RefreshTokens refreshes an xAI access token.
func (a *XAIAuth) RefreshTokens(ctx context.Context, refreshToken, tokenEndpoint string) (*TokenData, error) {
if strings.TrimSpace(refreshToken) == "" {
return nil, fmt.Errorf("xai token refresh: refresh token is required")
}
if ctx == nil {
ctx = context.Background()
}
refreshToken = strings.TrimSpace(refreshToken)
if strings.TrimSpace(tokenEndpoint) == "" {
discovery, errDiscover := a.Discover(ctx)
if errDiscover != nil {
return nil, errDiscover
}
tokenEndpoint = discovery.TokenEndpoint
}
tokenEndpoint = strings.TrimSpace(tokenEndpoint)
result, err, _ := xaiRefreshGroup.Do(refreshToken, func() (interface{}, error) {
return a.refreshTokensSingleFlight(context.WithoutCancel(ctx), refreshToken, tokenEndpoint)
})
if err != nil {View on GitHub (pinned to 78f0c4079e)
Solutions
- Inspect the stored auth/credential file and confirm the refresh token field exists and is non-empty
- If the device flow never returned a refresh_token, re-authenticate interactively to obtain one
- Guard refresh scheduling on the presence of a refresh token before invoking RefreshTokens
Example fix
// before
if err := svc.Refresh(ctx); err != nil { ... } // internally calls RefreshTokens with empty token
// after
if strings.TrimSpace(creds.RefreshToken) == "" {
return errors.New("no refresh token stored; re-authentication required")
}
if err := svc.Refresh(ctx); err != nil { ... } Defensive patterns
Strategy: validation
Validate before calling
if strings.TrimSpace(storedTokenData.RefreshToken) == "" {
return fmt.Errorf("no xAI refresh token stored; run interactive login again")
}
newTokens, err := auth.RefreshTokens(ctx, storedTokenData.RefreshToken, storedTokenData.TokenEndpoint) Try / catch
if err != nil && strings.Contains(err.Error(), "refresh token is required") {
// stored credentials incomplete: force re-auth instead of retrying
return fmt.Errorf("re-authentication required: %w", err)
} Prevention
- Validate stored credential files contain a refresh token before scheduling refresh
- Persist TokenData from the device flow atomically so partial writes never drop the refresh token
- Fall back to interactive login when the refresh token is absent rather than retrying
When it happens
Trigger: Calling XAIAuth.RefreshTokens(ctx, "", endpoint), or passing a TokenData.RefreshToken that was never populated (e.g. the device flow returned no refresh_token).
Common situations: Persisted auth file missing the refresh token field; token refresh job loading stale/corrupted auth state; xAI issuing access-token-only responses for some grants.
Related errors
- source auth path is empty
- auth path is empty
- auth id is empty
- select Claude device ID: session ID is empty
- kimi: refresh token is required
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/92f4a8b12e943856.
Report an issue: GitHub.