{"record":{"id":"38a8024569b58e03","repo":"siyuan-note/siyuan","slug":"exchange-oauth-authorization-code-w","errorCode":null,"errorMessage":"exchange OAuth authorization code: %w","messagePattern":"exchange OAuth authorization code: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":400,"sourceCode":"\tcase <-timer.C:\n\t\treturn fmt.Errorf(\"OAuth authorization timed out\")\n\t}\n\tif callback.Error != \"\" {\n\t\treturn fmt.Errorf(\"OAuth authorization failed: %s\", callback.Error)\n\t}\n\tif callback.State != state {\n\t\treturn fmt.Errorf(\"OAuth state mismatch\")\n\t}\n\tif callback.Code == \"\" {\n\t\treturn fmt.Errorf(\"OAuth callback did not include an authorization code\")\n\t}\n\n\texchangeCtx := context.WithValue(ctx, oauth2.HTTPClient, h.client)\n\ttoken, err := config.Exchange(exchangeCtx, callback.Code,\n\t\toauth2.VerifierOption(verifier),\n\t\toauth2.SetAuthURLParam(\"resource\", prm.Resource))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"exchange OAuth authorization code: %w\", err)\n\t}\n\tif token.TokenType != \"\" && !strings.EqualFold(token.TokenType, \"Bearer\") {\n\t\treturn fmt.Errorf(\"OAuth token endpoint returned unsupported token type %q\", token.TokenType)\n\t}\n\tcredential = registrationCredential\n\tcredential.TokenAuthMethod = authMethod\n\tcredential.AccessToken = token.AccessToken\n\tcredential.RefreshToken = token.RefreshToken\n\tcredential.TokenType = token.TokenType\n\tcredential.Expiry = token.Expiry\n\tcredential.Scopes = scopes\n\tcredential.Rejected = false\n\tif err = putOAuthCredential(credential); err != nil {\n\t\treturn fmt.Errorf(\"save OAuth credentials: %w\", err)\n\t}\n\th.sourceMu.Lock()\n\th.source = &storedOAuthTokenSource{credential: credential, client: h.client}\n\th.sourceMu.Unlock()","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/oauth.go#L382-L418","documentation":"After receiving the authorization code, the library exchanges it at the token endpoint using PKCE (verifier) and the resource parameter. Any failure of the underlying oauth2 Config.Exchange call (network error, invalid_grant, invalid_client, expired code, etc.) is wrapped as 'exchange OAuth authorization code: <cause>'.","triggerScenarios":"config.Exchange(exchangeCtx, callback.Code, VerifierOption, SetAuthURLParam(resource)) returns an error — e.g. HTTP 400/401 from the token endpoint, network failure, or context cancellation during the request.","commonSituations":"Authorization code expired or already redeemed (invalid_grant) after a retry; client authentication rejected because the registered method (basic/post/none) doesn't match the server's expectation; clock skew; the resource indicator mismatches the server audience; token endpoint unreachable.","solutions":["Inspect the wrapped cause for the exact OAuth error (invalid_grant, invalid_client, ...) and fix accordingly","Perform a fresh full authorization instead of reusing/replaying the code — codes are single-use","Verify the registered token_endpoint_auth_method matches how the server expects the client to authenticate","Check network reachability of the token endpoint and system clock accuracy","Confirm the resource parameter/audience configured on the server matches the protected resource"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm the token endpoint is reachable before starting the flow\nresp, err := h.client.Head(asm.TokenEndpoint)\nif err != nil { /* token endpoint unreachable: fix network first */ }","typeGuard":null,"tryCatchPattern":"if err := h.Authorize(ctx, true); err != nil {\n    if strings.Contains(err.Error(), \"exchange OAuth authorization code\") {\n        var retrieveErr *oauth2.RetrieveError\n        if errors.As(err, &retrieveErr) {\n            // inspect retrieveErr.ErrorCode (invalid_grant, invalid_client, ...)\n        }\n    }\n}","preventionTips":["Never replay authorization codes; run a full fresh flow each time","Keep the registered token_endpoint_auth_method consistent with server expectations","Keep system clocks accurate (code/token lifetime validation)","Confirm the resource/audience indicator matches the protected resource configuration"],"tags":["oauth","mcp","token-exchange","pkce"],"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"}