{"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":"exception","errorClass":null,"httpStatus":null,"severity":"error","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/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/oauth.go#L220-L256","documentation":"The stored credential has a refresh token and the code attempted refreshOAuthCredential, but the refresh failed with a non-permanent (retryable) error — e.g. network failure, 5xx, or timeout at the token endpoint. Because the error is not permanent, the library keeps the stored tokens and surfaces the failure instead of wiping credentials.","triggerScenarios":"Authorize is called with a challenge error other than insufficient_scope, a valid non-expired registration, and a stored RefreshToken; the token endpoint returns a transient error (connection refused, 500, timeout) during the refresh_token grant.","commonSituations":"IdP briefly down or rate-limiting the token endpoint; network drop between SiYuan and the IdP; token endpoint TLS cert rotation causing transient failures; clock skew causing intermittent validation errors.","solutions":["Retry Authorize after confirming the token endpoint is reachable (curl the token URL)","Check the wrapped refreshErr for the underlying cause (network vs HTTP status) and fix that","If the IdP rejected the grant permanently (invalid_grant), clear the stored MCP OAuth credential for this server so a fresh authorization flow runs","Verify system clock correctness if the underlying error mentions token validity"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := h.Authorize(ctx, req, resp); err != nil && strings.Contains(err.Error(), \"refresh OAuth credentials:\") {\n    // transient by design; schedule a retry with backoff\n    time.AfterFunc(backoff, retryAuthorize)\n}","preventionTips":["Keep the token endpoint reachable and monitor IdP health","Avoid aggressive request rates that trip token-endpoint rate limits","Keep system clocks synchronized (NTP) to prevent intermittent token validation failures","Clear stored credentials only when the refresh error is permanent, not transient"],"tags":["oauth","mcp","token-refresh","network"],"backgroundTag":"oauth-token-exchange-failed","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}