sipeed/picoclaw · error
no access token in response
Error message
no access token in response
What it means
parseTokenResponse (pkg/auth/oauth.go:583) decoded the body as JSON but access_token was missing or empty. The exchange technically 'succeeded' at the JSON level, yet there is no usable credential. Providers usually omit access_token only in error payloads or partial responses.
Source
Thrown at pkg/auth/oauth.go:583
return nil, fmt.Errorf("token exchange failed: %s", string(body))
}
return parseTokenResponse(body, provider)
}
func parseTokenResponse(body []byte, provider string) (*AuthCredential, error) {
var tokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
IDToken string `json:"id_token"`
}
if err := json.Unmarshal(body, &tokenResp); err != nil {
return nil, fmt.Errorf("parsing token response: %w", err)
}
if tokenResp.AccessToken == "" {
return nil, fmt.Errorf("no access token in response")
}
var expiresAt time.Time
if tokenResp.ExpiresIn > 0 {
expiresAt = time.Now().Add(time.Duration(tokenResp.ExpiresIn) * time.Second)
}
cred := &AuthCredential{
AccessToken: tokenResp.AccessToken,
RefreshToken: tokenResp.RefreshToken,
ExpiresAt: expiresAt,
Provider: provider,
AuthMethod: "oauth",
}
// Recent OpenAI OAuth responses may only include chatgpt_account_id in id_token claims.
if id := extractAccountID(tokenResp.IDToken); id != "" {
cred.AccountID = idView on GitHub (pinned to 49183d7e8d)
Solutions
- Log the (redacted) body to see exactly which fields the provider returned
- If it is an error envelope despite 200, treat the flow as failed and restart login; check the provider's docs for the payload shape
- If the field was renamed (e.g. accessToken), update the json tag in parseTokenResponse
- For id_token-only responses, decide whether validating id_token suffices for your flow
Example fix
// before
if tokenResp.AccessToken == "" {
return nil, fmt.Errorf("no access token in response")
}
// after (name the fields that were present)
if tokenResp.AccessToken == "" {
return nil, fmt.Errorf("no access token in response (fields present: %s)", strings.Join(presentFields(body), ", "))
} Defensive patterns
Strategy: type-guard
Validate before calling
// Confirm the key exists and is non-empty before trusting the flow
var probe struct {
AccessToken string `json:"access_token"`
}
if err := json.Unmarshal(body, &probe); err == nil && strings.TrimSpace(probe.AccessToken) == "" {
return fmt.Errorf("provider returned no access_token")
} Type guard
func isNoAccessTokenError(err error) bool {
return err != nil && strings.Contains(err.Error(), "no access token in response")
} Try / catch
cred, err := auth.ExchangeCodeForTokens(cfg, code, verifier, redirectURI)
if err != nil && isNoAccessTokenError(err) {
// 200-without-token usually means provider error envelope: restart login
return reloginFlow(cfg)
} Prevention
- Check for error objects in 200 responses when integrating new providers
- Re-login is the only sane recovery when no access token was issued
- Log which fields the provider did return to speed up support
- Pin provider API versions where possible
When it happens
Trigger: Token endpoint returns 200 with an error object like {"error":"..."} or a payload containing only id_token/refresh_token; an API change renaming access_token; a proxy rewriting the response.
Common situations: Provider 200-status error envelopes (nonstandard but seen in the wild); Google-style flows returning only an id_token for some grant types; partially stubbed test servers; field renaming after a provider API version bump.
Related errors
- parsing device code response: %w
- parsing token response: %w
- error fetching models: %w
- failed to unmarshal response: %w
- generating PKCE: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/11d88b29c1a0bc38.
Report an issue: GitHub.