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
- Read the wrapped *url.Error cause — it distinguishes dial/x509/proxyconnect precisely
- Print and validate tokenURL: cfg.TokenURL overrides cfg.Issuer+"/oauth/token", so a stale TokenURL is a classic culprit
- curl -v the token URL to confirm reachability from the same environment
- Configure proxy env vars or fix the URL; refresh is usually safe to retry with backoff once fixed
- 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
- Start refresh well before AccessToken expiry so retries fit in the window
- Validate TokenURL at config load; remember it overrides the issuer default
- Retry only transport errors — status failures need diagnosis, not retries
- Monitor connectivity to the token endpoint in long-running processes
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
- exchanging code for tokens: %w
- reading token refresh 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/06d301cc7b7820da.
Report an issue: GitHub.