{"record":{"id":"7235a5eab220f1ca","repo":"siyuan-note/siyuan","slug":"refresh-oauth-credentials-w","errorCode":null,"errorMessage":"refresh OAuth credentials: %w","messagePattern":"refresh OAuth credentials: %w","errorType":"http","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/mcp/client/oauth.go","lineNumber":238,"sourceCode":"\tif hasCredential && credential.Issuer == asm.Issuer {\n\t\tcredential.TokenEndpoint = asm.TokenEndpoint\n\t\tcredential.RevocationEndpoint = asm.RevocationEndpoint\n\t}\n\tif hasCredential && credential.Issuer == asm.Issuer && credential.RefreshToken != \"\" &&\n\t\tchallengeError != \"insufficient_scope\" && !credential.Rejected && !oauthClientRegistrationExpired(credential) {\n\t\trefreshed, permanent, refreshErr := refreshOAuthCredential(ctx, h.client, credential)\n\t\tif refreshErr == nil {\n\t\t\tif saveErr := putOAuthCredential(refreshed); saveErr != nil {\n\t\t\t\tlogging.LogWarnf(\"mcp oauth: save refreshed credentials failed: %s\", saveErr)\n\t\t\t}\n\t\t\th.sourceMu.Lock()\n\t\t\th.source = &storedOAuthTokenSource{credential: refreshed, client: h.client}\n\t\t\th.sourceMu.Unlock()\n\t\t\tsetMCPRuntimeStateForContext(ctx, h.server.ID, \"oauth_retrying\", 0, \"\", \"\")\n\t\t\treturn nil\n\t\t}\n\t\tif !permanent {\n\t\t\treturn fmt.Errorf(\"refresh OAuth credentials: %w\", refreshErr)\n\t\t}\n\t\tcredential.AccessToken = \"\"\n\t\tcredential.RefreshToken = \"\"\n\t\tcredential.Expiry = time.Time{}\n\t\tif saveErr := putOAuthCredential(credential); saveErr != nil {\n\t\t\tlogging.LogWarnf(\"mcp oauth: clear invalid credentials failed: %s\", saveErr)\n\t\t}\n\t}\n\tif !interactive {\n\t\tsetMCPRuntimeStateForContext(ctx, h.server.ID, \"authorization_required\", 0, \"\", \"\")\n\t\treturn errOAuthAuthorizationRequired\n\t}\n\tif !slices.Contains(asm.CodeChallengeMethodsSupported, \"S256\") {\n\t\treturn fmt.Errorf(\"OAuth authorization server does not support PKCE S256\")\n\t}\n\tif len(asm.ResponseTypesSupported) > 0 && !slices.Contains(asm.ResponseTypesSupported, \"code\") {\n\t\treturn fmt.Errorf(\"OAuth authorization server does not support the authorization code response type\")\n\t}","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/oauth.go#L220-L256","documentation":"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.","triggerScenarios":"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.","commonSituations":"Token endpoint briefly unavailable (5xx, maintenance); network blip between kernel and auth server; rate-limited token endpoint; clock skew causing a borderline retryable response.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Transient refresh failure: the refresh token was NOT cleared, so retry with backoff.\nif strings.Contains(err.Error(), \"refresh OAuth credentials\") {\n    // exponential backoff, then retry the connect\n}","preventionTips":["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."],"tags":["mcp","oauth","token-refresh","transient","retry"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}