siyuan-note/siyuan · warning
refresh OAuth credentials: %w
Error message
refresh OAuth credentials: %w
What it means
Returned by mcpOAuthHandler.Authorize when refreshOAuthCredential failed with a non-permanent ('transient') error. The branch only runs when a stored credential exists with a refresh token, matching issuer, not rejected, not registration-expired; refresh was attempted, failed, and the error was flagged as retryable (permanent == false). The wrapped refresh error is preserved. Permanent failures instead clear the credential and fall through to interactive authorization.
Source
Thrown at kernel/mcp/client/oauth.go:238
if hasCredential && credential.Issuer == asm.Issuer {
credential.TokenEndpoint = asm.TokenEndpoint
credential.RevocationEndpoint = asm.RevocationEndpoint
}
if hasCredential && credential.Issuer == asm.Issuer && credential.RefreshToken != "" &&
challengeError != "insufficient_scope" && !credential.Rejected && !oauthClientRegistrationExpired(credential) {
refreshed, permanent, refreshErr := refreshOAuthCredential(ctx, h.client, credential)
if refreshErr == nil {
if saveErr := putOAuthCredential(refreshed); saveErr != nil {
logging.LogWarnf("mcp oauth: save refreshed credentials failed: %s", saveErr)
}
h.sourceMu.Lock()
h.source = &storedOAuthTokenSource{credential: refreshed, client: h.client}
h.sourceMu.Unlock()
setMCPRuntimeStateForContext(ctx, h.server.ID, "oauth_retrying", 0, "", "")
return nil
}
if !permanent {
return fmt.Errorf("refresh OAuth credentials: %w", refreshErr)
}
credential.AccessToken = ""
credential.RefreshToken = ""
credential.Expiry = time.Time{}
if saveErr := putOAuthCredential(credential); saveErr != nil {
logging.LogWarnf("mcp oauth: clear invalid credentials failed: %s", saveErr)
}
}
if !interactive {
setMCPRuntimeStateForContext(ctx, h.server.ID, "authorization_required", 0, "", "")
return errOAuthAuthorizationRequired
}
if !slices.Contains(asm.CodeChallengeMethodsSupported, "S256") {
return fmt.Errorf("OAuth authorization server does not support PKCE S256")
}
if len(asm.ResponseTypesSupported) > 0 && !slices.Contains(asm.ResponseTypesSupported, "code") {
return fmt.Errorf("OAuth authorization server does not support the authorization code response type")
}View on GitHub (pinned to 251596fc0d)
Solutions
- Retry the connect after a short backoff — the error is transient and the stored refresh token was NOT cleared.
- Check the wrapped error for the token endpoint's status; if it is a network/timeout error, verify connectivity to the token endpoint.
- If the wrapped error is a 5xx from the auth server, wait for it to recover; do NOT re-authorize, because the refresh token is still valid.
- If retries consistently fail with the same transient error, capture the token endpoint response (status + body) to determine why refresh is rejected.
Defensive patterns
Strategy: retry
Try / catch
// Transient refresh failure: the refresh token was NOT cleared, so retry with backoff.
if strings.Contains(err.Error(), "refresh OAuth credentials") {
// exponential backoff, then retry the connect
} Prevention
- Retry transient refresh errors; do not force re-authorization (the refresh token is still valid).
- Monitor token endpoint availability; 5xx/network errors are the usual transient cause.
- Capture the token endpoint status/body when retries repeatedly fail.
When it happens
Trigger: Stored OAuth credential matches the auth server, refresh is attempted via refreshOAuthCredential; the token endpoint returns a transient failure (network timeout, 5xx, temporary unavailability). refreshOAuthCredential returns (nil, false, err) and Authorize wraps it.
Common situations: Token endpoint briefly unavailable (5xx, maintenance); network blip between kernel and auth server; rate-limited token endpoint; clock skew causing a borderline retryable response.
Related errors
- mcp oauth authorization required
- parse OAuth challenge: %w
- server returned %s without an OAuth Bearer challenge
- server returned %s
- discover OAuth authorization server: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/7235a5eab220f1ca.
Report an issue: GitHub.