sipeed/picoclaw · error

refreshing token: %w

Error message

refreshing token: %w

What it means

RefreshAccessToken (pkg/auth/oauth.go:460) failed at the transport level: http.PostForm to tokenURL (cfg.TokenURL if set, else {Issuer}/oauth/token) never produced a response. The wrapped error is a *url.Error naming the real cause such as DNS, refused connection, TLS, or proxy failure.

Source

Thrown at pkg/auth/oauth.go:460

	data := url.Values{
		"client_id":     {cfg.ClientID},
		"grant_type":    {"refresh_token"},
		"refresh_token": {cred.RefreshToken},
		"scope":         {"openid profile email"},
	}
	if cfg.ClientSecret != "" {
		data.Set("client_secret", cfg.ClientSecret)
	}

	tokenURL := cfg.Issuer + "/oauth/token"
	if cfg.TokenURL != "" {
		tokenURL = cfg.TokenURL
	}

	resp, err := http.PostForm(tokenURL, data)
	if err != nil {
		return nil, fmt.Errorf("refreshing token: %w", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading token refresh response: %w", err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("token refresh failed: %s", string(body))
	}

	refreshed, err := parseTokenResponse(body, cred.Provider)
	if err != nil {
		return nil, err
	}
	if refreshed.RefreshToken == "" {
		refreshed.RefreshToken = cred.RefreshToken
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Read the wrapped *url.Error cause — it distinguishes dial/x509/proxyconnect precisely
  2. Print and validate tokenURL: cfg.TokenURL overrides cfg.Issuer+"/oauth/token", so a stale TokenURL is a classic culprit
  3. curl -v the token URL to confirm reachability from the same environment
  4. Configure proxy env vars or fix the URL; refresh is usually safe to retry with backoff once fixed
  5. Fall back to re-login if the credential is near expiry and refresh cannot succeed
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 == "" || u.Host == "" {
	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

refreshed, err := auth.RefreshAccessToken(cred, cfg)
if err != nil && isTransportError(err) {
	// transient network failure: safe to retry with backoff
	time.Sleep(2 * time.Second)
	refreshed, err = auth.RefreshAccessToken(cred, cfg)
}

Prevention

When it happens

Trigger: Calling RefreshAccessToken while offline, with a bad Issuer/TokenURL (typo, wrong scheme), through a required-but-unconfigured proxy, or against a TLS endpoint whose cert the client rejects.

Common situations: Token refresh attempted in CI/offline daemons with no egress; TokenURL left over from another environment; HTTPS_PROXY unset behind a corporate firewall; issuer certificate rotation breaking x509 verification.

Related errors


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