router-for-me/CLIProxyAPI · error

xai device token response missing access_token

Error message

xai device token response missing access_token

What it means

The exchange technically succeeded (no error field, status 200) but access_token is empty, which the client treats as a hard failure since TokenData cannot be built without it. This indicates a malformed or surprising success response from the token endpoint.

Source

Thrown at internal/auth/xai/xai.go:323

			return nil, nil, nextInterval, true
		case "expired_token":
			return nil, fmt.Errorf("xai device code expired"), interval, false
		case "access_denied":
			return nil, fmt.Errorf("xai device authorization denied"), interval, false
		default:
			desc := strings.TrimSpace(payload.ErrorDescription)
			if desc != "" {
				return nil, fmt.Errorf("xai device token error: %s: %s", payload.Error, desc), interval, false
			}
			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 {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Log the full body to see what the 'success' response actually contained
  2. Confirm via discovery that the token endpoint is the real OAuth token URL
  3. If xAI changed field names, update the payload struct json tags in exchangeDeviceCode
Defensive patterns

Strategy: try-catch

Try / catch

if err != nil && strings.Contains(err.Error(), "missing access_token") {
    log.Errorf("token endpoint returned 200 without access_token; verify endpoint URL and xAI schema: %v", err)
    return err
}

Prevention

When it happens

Trigger: 200 JSON from the token endpoint lacking access_token — e.g. {"token_type":"Bearer"} or an unrelated JSON object.

Common situations: Wrong endpoint that returns 200 JSON for everything; response schema change by xAI; a proxy returning a success-shaped health-check body.

Related errors


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