{"record":{"id":"bd22070949aeedfc","repo":"siyuan-note/siyuan","slug":"oauth-token-endpoint-returned-s","errorCode":null,"errorMessage":"OAuth token endpoint returned %s","messagePattern":"OAuth token endpoint returned (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":678,"sourceCode":"\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\treq.Header.Set(\"Accept\", \"application/json\")\n\tapplyOAuthClientAuthentication(nil, req, credential)\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdefer resp.Body.Close()\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\ttokenErr := &oauthTokenError{}\n\t\tif json.Unmarshal(body, tokenErr) != nil || tokenErr.Code == \"\" {\n\t\t\treturn nil, nil, fmt.Errorf(\"OAuth token endpoint returned %s\", resp.Status)\n\t\t}\n\t\treturn nil, tokenErr, tokenErr\n\t}\n\tresult := &oauthTokenResponse{}\n\tif err = json.Unmarshal(body, result); err != nil {\n\t\treturn nil, nil, err\n\t}\n\tif result.AccessToken == \"\" {\n\t\treturn nil, nil, fmt.Errorf(\"OAuth token endpoint returned no access token\")\n\t}\n\tif result.TokenType != \"\" && !strings.EqualFold(result.TokenType, \"Bearer\") {\n\t\treturn nil, nil, fmt.Errorf(\"OAuth token endpoint returned unsupported token type %q\", result.TokenType)\n\t}\n\treturn result, nil, nil\n}\n\nfunc applyOAuthClientAuthentication(values url.Values, req *http.Request, credential oauthCredential) {\n\tswitch credential.TokenAuthMethod {","sourceCodeStart":660,"sourceCodeEnd":696,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/oauth.go#L660-L696","documentation":"oauthTokenRequest POSTs to the token endpoint and expects either a 2xx JSON token response or an RFC 6749 JSON error body with a non-empty 'error' code. When the status is non-2xx AND the body is not a parseable OAuth error (or lacks the error code), it fails with this generic message including the HTTP status text, because the server's actual rejection reason is unknown.","triggerScenarios":"Called from refreshOAuthCredential (grant_type=refresh_token) when the token endpoint returns e.g. 500, 403, 404, or an HTML/empty error body with a non-2xx status — anything outside 200-299 that cannot be decoded into an oauthTokenError with a code.","commonSituations":"Token endpoint URL wrong (404 from a router); IdP returns HTML error page (WAF/Cloudflare challenge); 500 during IdP outage; auth method rejected with a non-protocol 401 body; rate limiting returning plain text.","solutions":["Verify credential.TokenEndpoint matches the token_endpoint advertised in the authorization server metadata.","Capture the raw response body (curl the token endpoint) to see the real error page/message.","Check for WAF/proxy interference (Cloudflare challenges, SSO login HTML) between the client and IdP.","Confirm TokenAuthMethod (none/client_secret_post/client_secret_basic) is one the IdP accepts; a wrong method can yield opaque 401s.","Retry during IdP outages; 5xx is usually transient."],"exampleFix":"// before: stale endpoint after IdP migration\ntoken_endpoint: \"https://old-idp.example.com/oauth/token\"\n// after: use current metadata value\ntoken_endpoint: \"https://auth.example.com/oauth/token\"","handlingStrategy":"try-catch","validationCode":"resp, err := http.PostForm(tokenEndpoint, url.Values{\"grant_type\": {\"refresh_token\"}, \"refresh_token\": {tok}})\nif err != nil || resp.StatusCode < 200 || resp.StatusCode >= 300 {\n    // probe the token endpoint manually to see the raw error before refreshing\n}","typeGuard":null,"tryCatchPattern":"cred, permanent, err := refreshOAuthCredential(ctx, client, credential)\nif err != nil {\n    if permanent {\n        startFreshAuthorizeFlow(server) // invalid_grant/invalid_client\n    } else {\n        inspectTokenEndpointRawResponse() // non-protocol error, check body/status\n    }\n}","preventionTips":["Verify token_endpoint against authorization server metadata during setup","Test the token endpoint with curl to see raw non-JSON error bodies","Confirm the configured token auth method is supported by the IdP","Check WAF/proxy behavior on POSTs to the token endpoint"],"tags":["oauth","http","token-endpoint","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"}