sipeed/picoclaw · error
exchanging code for tokens: %w
Error message
exchanging code for tokens: %w
What it means
ExchangeCodeForTokens (pkg/auth/oauth.go:556) failed before getting an HTTP response: http.PostForm to tokenURL (cfg.TokenURL or {Issuer}/oauth/token) with the authorization_code grant could not connect. The wrapped *url.Error carries the transport-level cause.
Source
Thrown at pkg/auth/oauth.go:556
}
if cfg.ClientSecret != "" {
data.Set("client_secret", cfg.ClientSecret)
}
tokenURL := cfg.Issuer + "/oauth/token"
if cfg.TokenURL != "" {
tokenURL = cfg.TokenURL
}
// Determine provider name from config
provider := "openai"
if cfg.TokenURL != "" && strings.Contains(cfg.TokenURL, "googleapis.com") {
provider = "google-antigravity"
}
resp, err := http.PostForm(tokenURL, data)
if err != nil {
return nil, fmt.Errorf("exchanging code for tokens: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading token exchange response: %w", err)
}
if resp.StatusCode != http.StatusOK {
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"`View on GitHub (pinned to 49183d7e8d)
Solutions
- Read the wrapped *url.Error to identify dial/TLS/proxy failure
- Verify tokenURL (print it — cfg.TokenURL overrides the issuer-derived default)
- Restore connectivity, then retry the exchange promptly — authorization codes are short-lived; if expired, restart the login flow
- Set proxy env vars if egress requires them
Defensive patterns
Strategy: retry
Validate before calling
tokenURL := cfg.TokenURL
if tokenURL == "" {
tokenURL = cfg.Issuer + "/oauth/token"
}
if u, err := url.Parse(tokenURL); err != nil || u.Scheme != "https" {
return fmt.Errorf("invalid token URL %q", tokenURL)
} Type guard
func isTransportError(err error) bool {
var ue *url.Error
return errors.As(err, &ue)
} Try / catch
cred, err := auth.ExchangeCodeForTokens(cfg, code, verifier, redirectURI)
if err != nil && isTransportError(err) && time.Since(codeIssuedAt) < 5*time.Minute {
// code still fresh: one retry is reasonable
cred, err = auth.ExchangeCodeForTokens(cfg, code, verifier, redirectURI)
} Prevention
- Retry exchange quickly on transport errors — codes expire in minutes
- Validate TokenURL and Issuer at startup
- Ensure proxy/TLS config is correct before starting the login flow
- If retry fails with invalid_grant, restart login instead of retrying again
When it happens
Trigger: Exchanging the code right after browser callback while offline; Issuer/TokenURL malformed or unreachable; proxy or TLS failure on the token endpoint; DNS outage between callback and exchange.
Common situations: User's network drops between authorizing in the browser and the CLI exchanging the code; TokenURL misconfigured; corporate SSL inspection rejecting the cert; the auth code typically expires in minutes, so fixing the network fast still matters.
Related errors
- refreshing token: %w
- reading token exchange response: %w
- error fetching models: %w
- request failed: %w
- reading device token response: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/9528348b8642b103.
Report an issue: GitHub.