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

  1. Inspect the stored auth/credential file and confirm the refresh token field exists and is non-empty
  2. If the device flow never returned a refresh_token, re-authenticate interactively to obtain one
  3. 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

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


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/92f4a8b12e943856. Report an issue: GitHub.