{"record":{"id":"c0d68be921a16b77","repo":"siyuan-note/siyuan","slug":"oauth-token-endpoint-returned-no-access-token","errorCode":null,"errorMessage":"OAuth token endpoint returned no access token","messagePattern":"OAuth token endpoint returned no access token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/oauth.go","lineNumber":687,"sourceCode":"\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 {\n\tcase \"client_secret_basic\":\n\t\tif req != nil {\n\t\t\treq.SetBasicAuth(url.QueryEscape(credential.ClientID), url.QueryEscape(credential.ClientSecret))\n\t\t}\n\tdefault:\n\t\tif values != nil {\n\t\t\tvalues.Set(\"client_id\", credential.ClientID)\n\t\t\tif credential.TokenAuthMethod == \"client_secret_post\" && credential.ClientSecret != \"\" {\n\t\t\t\tvalues.Set(\"client_secret\", credential.ClientSecret)","sourceCodeStart":669,"sourceCodeEnd":705,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/oauth.go#L669-L705","documentation":"The token endpoint returned a 2xx response, but the JSON body's `access_token` field was empty. SiYuan refuses to treat a token response as valid when the access token itself is missing, since the subsequent API call would have no credential to send.","triggerScenarios":"`oauthTokenResponse.AccessToken` is empty after a successful `json.Unmarshal`. The IdP returned 200 with a body that omits `access_token` (e.g. it returned only an `id_token`, an error wrapped in 200, or a non-OAuth JSON shape).","commonSituations":"The server is not actually an OAuth 2.0 token endpoint (pointed at a userinfo or userinfo-like endpoint). The grant returned an error that the IdP wrapped with a 200 status. The `Accept` header negotiation returned a non-token content type whose JSON lacks the field.","solutions":["Capture and print the raw token-endpoint response body to confirm it is a valid OAuth token JSON with an `access_token` field.","Verify the endpoint URL is the token endpoint, not a userinfo or introspection endpoint.","Re-check the `grant_type` and parameters sent so the IdP actually issues an access token."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Decode the token response defensively and require access_token before use.\nvar tok oauthTokenResponse\nif err := json.Unmarshal(body, &tok); err != nil {\n    return fmt.Errorf(\"token response not JSON: %w\", err)\n}\nif strings.TrimSpace(tok.AccessToken) == \"\" {\n    return fmt.Errorf(\"no access_token in body: %s\", string(body))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Point the token endpoint at the real OAuth token URL, not userinfo/introspection.","Inspect the raw body when integration fails to confirm the expected fields are present.","Ensure grant_type and parameters actually cause the IdP to issue an access token."],"tags":["oauth","mcp","identity-provider","token"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}