{"record":{"id":"5c12500fd4090395","repo":"siyuan-note/siyuan","slug":"server-returned-s","errorCode":null,"errorMessage":"server returned %s","messagePattern":"server returned (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":196,"sourceCode":"\t\tRefreshToken: credential.RefreshToken,\n\t\tExpiry:       credential.Expiry,\n\t}\n}\n\nfunc (h *mcpOAuthHandler) Authorize(ctx context.Context, req *http.Request, resp *http.Response) (retErr error) {\n\tdefer resp.Body.Close()\n\tdefer io.Copy(io.Discard, io.LimitReader(resp.Body, 1<<20))\n\n\tchallenges, err := oauthex.ParseWWWAuthenticate(resp.Header.Values(\"WWW-Authenticate\"))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"parse OAuth challenge: %w\", err)\n\t}\n\tif !hasBearerChallenge(challenges) {\n\t\treturn fmt.Errorf(\"server returned %s without an OAuth Bearer challenge\", resp.Status)\n\t}\n\tchallengeError := bearerChallengeParam(challenges, \"error\")\n\tif resp.StatusCode == http.StatusForbidden && challengeError != \"insufficient_scope\" {\n\t\treturn fmt.Errorf(\"server returned %s\", resp.Status)\n\t}\n\tinteractive := h.interactive.Load()\n\tif interactive {\n\t\tdefer func() {\n\t\t\tif retErr != nil && !errors.Is(retErr, context.Canceled) {\n\t\t\t\tsetMCPRuntimeStateForContext(ctx, h.server.ID, \"authorization_required\", 0, retErr.Error(), \"\")\n\t\t\t}\n\t\t}()\n\t}\n\n\tprm, err := discoverProtectedResource(ctx, challenges, req.URL.String(), h.client)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tasm, err := auth.GetAuthServerMetadata(ctx, prm.AuthorizationServers[0], h.client)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"discover OAuth authorization server: %w\", err)","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/oauth.go#L178-L214","documentation":"Returned by mcpOAuthHandler.Authorize when the response is HTTP 403, the Bearer challenge carried an 'error' parameter that is NOT 'insufficient_scope', and thus cannot be resolved by re-authenticating the user. The status text is embedded via %s. The rationale is that 403 with insufficient_scope is retryable via token refresh, but other 403s (e.g. invalid_token, insufficient_user_privileges) are terminal for this credential.","triggerScenarios":"MCP server returns 401 with a Bearer challenge whose 'error' param is set and not insufficient_scope, AND the response status (after re-evaluation) is 403 — the guard checks resp.StatusCode == http.StatusForbidden && challengeError != \"insufficient_scope\".","commonSituations":"User's account lacks the required role/permission (insufficient_user_privileges); token revoked server-side (invalid_token) but reported as 403; access control policy denies the principal entirely.","solutions":["Distinguish the challenge 'error' value: for invalid_token / revoked tokens, clear the stored credential and re-run the interactive OAuth flow to obtain a fresh token.","For insufficient_user_privileges or policy denials, the account itself is not authorized — request access from the MCP server operator; no client-side refresh will help.","If the server is misclassifying an insufficient_scope condition as a different error, report it upstream so the retry path can engage."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Terminal 403: do not retry OAuth; surface to user / audit account permissions.\nif strings.Contains(err.Error(), \"server returned 403\") {\n    // stop retry, request operator review of account permissions\n}","preventionTips":["Distinguish insufficient_scope (retryable via refresh) from other 403 errors (terminal).","Review the user account's role/permissions on the MCP server when this fires."],"tags":["mcp","oauth","authorization","http-status"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}