{"record":{"id":"9aad2bc8a65be829","repo":"siyuan-note/siyuan","slug":"mcp-oauth-authorization-required","errorCode":null,"errorMessage":"mcp oauth authorization required","messagePattern":"mcp oauth authorization required","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/mcp/client/oauth.go","lineNumber":47,"sourceCode":"\t\"net/url\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/modelcontextprotocol/go-sdk/auth\"\n\t\"github.com/modelcontextprotocol/go-sdk/oauthex\"\n\t\"github.com/siyuan-note/httpclient\"\n\t\"github.com/siyuan-note/logging\"\n\t\"github.com/siyuan-note/siyuan/kernel/conf\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n\t\"golang.org/x/oauth2\"\n)\n\nconst oauthAuthorizationTimeout = 5 * time.Minute\n\nvar errOAuthAuthorizationRequired = errors.New(\"mcp oauth authorization required\")\n\ntype oauthCallbackResult struct {\n\tCode  string\n\tState string\n\tError string\n}\n\ntype oauthFlow struct {\n\tState   string\n\tIssuer  string\n\tResult  chan oauthCallbackResult\n\tExpires time.Time\n}\n\nvar oauthFlows = struct {\n\tsync.Mutex\n\titems map[string]*oauthFlow\n}{items: map[string]*oauthFlow{}}","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/oauth.go#L29-L65","documentation":"errOAuthAuthorizationRequired is a sentinel error signaling that the remote MCP server responded with an OAuth challenge and the client needs the user to complete interactive authorization before the connection can succeed. Callers detect it with errors.Is to treat it as a control-flow signal (abort this connect attempt quietly, prompt for authorization, or mark credentials rejected) rather than a hard failure.","triggerScenarios":"During connectOneServer the HTTP transport's OAuth handler receives a 401/403 with a WWW-Authenticate Bearer challenge and no valid cached token; the handler returns this sentinel so the connect loop can pause for interactive authorization.","commonSituations":"First connection to an OAuth-protected MCP server with no stored token; a stored access token expired or was revoked; the server rotated its authorization requirements; running non-interactively so the browser-based flow cannot complete.","solutions":["Trigger the interactive OAuth authorization flow for this server (reconnect with interactive mode so the browser authorization can run)","If stored credentials are rejected, clear/re-authorize the server's OAuth credentials (markOAuthCredentialRejected path) and authorize again","Check that the server's authorization/redirect endpoints are reachable from the browser"],"exampleFix":"// caller pattern\nif errors.Is(err, errOAuthAuthorizationRequired) {\n    // prompt the user to authorize, then retry the connection\n    return nil\n}","handlingStrategy":"try-catch","validationCode":"// before connecting: check whether the server needs OAuth and whether a token exists\nif !hasAuthorizationHeader(server.Headers) && serverRequiresOAuth(server.URL) && !hasStoredToken(server.ID) {\n    // plan an interactive authorization step\n}","typeGuard":null,"tryCatchPattern":"err := connectOneServer(ctx, server, false)\nswitch {\ncase errors.Is(err, errOAuthAuthorizationRequired):\n    // run interactive OAuth authorization, then retry the connect\n    retryWithAuthorization(server)\ncase err != nil && setOAuthRetryStateForError(ctx, server.ID, err.Error()):\n    markOAuthCredentialRejected(server.ID, server.URL)\n}","preventionTips":["Always compare with errors.Is(err, errOAuthAuthorizationRequired), never string equality","Proactively refresh tokens before they expire","Keep an interactive re-authorization path available for OAuth-protected servers","Detect and clear rejected credentials so the next connect triggers a fresh flow"],"tags":["mcp","oauth","authentication"],"backgroundTag":"authentication-required","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"}