router-for-me/CLIProxyAPI · error
failed to parse token response: %w
Error message
failed to parse token response: %w
What it means
The token exchange returned HTTP 200 but the body was not valid JSON for the expected token shape — json.Unmarshal failed. A 200 with malformed JSON usually means an intermediary (captive portal, proxy, antivirus) rewrote the response, or the endpoint returned an unexpected content type like HTML. The struct expects access_token/refresh_token/id_token fields; missing fields alone do NOT trigger this error, only syntactically invalid JSON does.
Source
Thrown at internal/auth/codex/openai_auth.go:152
return nil, fmt.Errorf("failed to read token response: %w", err)
}
// log.Debugf("Token response: %s", string(body))
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("token exchange failed with status %d: %s", resp.StatusCode, string(body))
}
// Parse token response
var tokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
IDToken string `json:"id_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
if err = json.Unmarshal(body, &tokenResp); err != nil {
return nil, fmt.Errorf("failed to parse token response: %w", err)
}
// Extract account ID from ID token
claims, err := ParseJWTToken(tokenResp.IDToken)
if err != nil {
log.Warnf("Failed to parse ID token: %v", err)
}
accountID := ""
email := ""
if claims != nil {
accountID = claims.GetAccountID()
email = claims.GetUserEmail()
}
// Create token data
tokenData := CodexTokenData{
IDToken: tokenResp.IDToken,View on GitHub (pinned to 78f0c4079e)
Solutions
- Log or inspect the raw body (temporarily re-enable the commented log.Debugf line) to see what was actually returned.
- Identify and bypass the intermediary — captive portal login first, or exclude the auth host from the MITM proxy.
- Retry from a clean network to confirm the response is provider JSON.
- Report upstream if the raw body is genuinely malformed provider output.
Example fix
// before
// log.Debugf("Token response: %s", string(body))
// after (temporary debugging)
log.Debugf("Token response: %s", string(body)) Defensive patterns
Strategy: try-catch
Try / catch
tok, err := auth.ExchangeCode(ctx, code)
if err != nil && strings.Contains(err.Error(), "failed to parse token response") {
// 200 + non-JSON: almost always a portal/proxy rewriting the response
log.Errorf("auth endpoint returned non-JSON; check captive portal/proxy: %v", err)
} Prevention
- Complete captive-portal authentication before starting OAuth login.
- Bypass MITM proxies for the provider's auth domain.
- Enable debug body logging when diagnosing parse failures.
When it happens
Trigger: Captive portal or proxy injecting an HTML page with status 200; response body truncated to invalid JSON; endpoint returning a JSON error envelope that is syntactically fine but semantically empty (that would parse, so true triggers are syntax-level); encoding mismatch after TLS interception.
Common situations: Hotel/airport Wi-Fi captive portals; MITM proxies that rewrite auth-domain responses; rare upstream incidents serving error pages with 200.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- token exchange request failed: %w
- failed to parse refresh response: %w
- failed to parse response JSON: %w
- decode response: %w
- port %d is already in use
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/7147d89735472069.
Report an issue: GitHub.