router-for-me/CLIProxyAPI · error
refresh response did not include access_token
Error message
refresh response did not include access_token
What it means
After successfully calling RefreshTokensWithRetry in cmd/fetch_codex_models/main.go, the refresh response is inspected for an access token. If `tokenData.AccessToken` is empty/whitespace after trimming, the tool aborts with `refresh response did not include access_token`: the OAuth token endpoint answered 200 but its payload did not contain the expected field, so persisting it would produce an unusable credential.
Source
Thrown at cmd/fetch_codex_models/main.go:203
return accessToken, false, nil
}
}
refreshToken := metaStringValue(auth.Metadata, "refresh_token")
if refreshToken == "" {
if accessToken != "" {
return accessToken, false, nil
}
return "", false, fmt.Errorf("missing access_token and refresh_token")
}
svc := codexauth.NewCodexAuthWithProxyURL(nil, auth.ProxyURL)
tokenData, errRefresh := svc.RefreshTokensWithRetry(ctx, refreshToken, 3)
if errRefresh != nil {
return "", false, errRefresh
}
if strings.TrimSpace(tokenData.AccessToken) == "" {
return "", false, fmt.Errorf("refresh response did not include access_token")
}
if auth.Metadata == nil {
auth.Metadata = make(map[string]any)
}
auth.Metadata["id_token"] = tokenData.IDToken
auth.Metadata["access_token"] = tokenData.AccessToken
if tokenData.RefreshToken != "" {
auth.Metadata["refresh_token"] = tokenData.RefreshToken
}
if tokenData.AccountID != "" {
auth.Metadata["account_id"] = tokenData.AccountID
}
if tokenData.Email != "" {
auth.Metadata["email"] = tokenData.Email
}
auth.Metadata["expired"] = tokenData.Expire
auth.Metadata["type"] = "codex"View on GitHub (pinned to 78f0c4079e)
Solutions
- Dump the raw refresh response (temporarily add logging) to see what the endpoint actually returned.
- Update the Codex client version string used for the refresh request to match the current Codex CLI.
- Remove intercepting proxies or configure proxy settings so the OAuth response is untouched.
- Re-run full OAuth login to mint a fresh refresh token, in case the old one triggers a degraded response.
- Check CLIProxyAPI issues/releases — an upstream API change likely needs a code fix in the codex auth package.
Defensive patterns
Strategy: fallback
Type guard
func isMissingAccessTokenError(err error) bool {
return err != nil && strings.Contains(err.Error(), "refresh response did not include access_token")
} Try / catch
token, refreshed, err := ensureToken(ctx, auth)
if isMissingAccessTokenError(err) {
// refresh token likely degraded: fall back to full re-login
err = reloginAndReplaceAuthFile(ctx, auth)
} Prevention
- Re-login fully when refresh responses start lacking access_token instead of retrying refresh.
- Keep the Codex client version current so the token endpoint contract matches.
- Avoid network middleware that rewrites OAuth responses.
When it happens
Trigger: OpenAI/Codex token endpoint returns a 200 body lacking `access_token` (e.g. only an id_token), the response shape changes after an API update, or a proxy in front mangles the JSON. Retry logic already ran (3 attempts) before this check, so it is not transient throttling.
Common situations: Upstream auth API contract change after a Codex client-version bump; an intercepting corporate proxy returning HTML/empty JSON; clock/protocol skew causing the endpoint to return an error body with 200; rare account states where refresh yields no access token.
Related errors
- missing access_token and refresh_token
- failed to save refreshed auth: %w
- models request failed with status %d: %s
- response JSON does not contain models array
- port %d is already in use
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/95d7185320858864.
Report an issue: GitHub.