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

  1. Read the wrapped *url.Error to identify dial/TLS/proxy failure
  2. Verify tokenURL (print it — cfg.TokenURL overrides the issuer-derived default)
  3. Restore connectivity, then retry the exchange promptly — authorization codes are short-lived; if expired, restart the login flow
  4. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/9528348b8642b103. Report an issue: GitHub.