{"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/251596fc0de2f9528c00c224252fd073a99973f4/kernel/mcp/client/oauth.go#L660-L696","documentation":"The OAuth token endpoint responded with a non-2xx HTTP status, and either the response body did not parse as an `oauthTokenError` JSON object or the parsed object had no `code` field. Because no structured error was available, SiYuan surfaces the raw HTTP status line (e.g. `400 Bad Request`).","triggerScenarios":"`oauthTokenRequest` POSTs to `credential.TokenEndpoint` and receives `statusCode < 200 || >= 300`. Occurs during initial token exchange or refresh-token rotation when the IdP rejects the request.","commonSituations":"Expired/already-used authorization code, wrong `client_secret`, mismatched `redirect_uri`, requested scopes the client is not allowed, or a transient IdP 5xx. A `TokenAuthMethod` (`client_secret_basic` vs `client_secret_post`) configured differently from what the IdP expects produces a 401.","solutions":["Inspect the full `resp.Status` value embedded in the error and the IdP's logs to find the precise rejection reason.","Verify `client_id`/`client_secret`, `redirect_uri`, and `scope` match the IdP's client registration exactly.","Confirm `TokenAuthMethod` matches the IdP's registered token-endpoint auth method.","If the body is JSON, the IdP may use a non-standard error shape; capture the raw body to decode it manually."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Inspect resp.Status; retry only transient (5xx/network) failures, not 4xx config errors.\nstatus := resp.Status\nif strings.HasPrefix(status, \"5\") || isNetworkErr(err) {\n    backoffRetry(ctx, exchange)\n} else {\n    logAndSurface(err) // 4xx: fix client config, do not retry\n}","typeGuard":null,"tryCatchPattern":"// Retry only transient token-endpoint failures; surface 4xx as config errors.\nvar lastErr error\nfor attempt := 0; attempt < 3; attempt++ {\n    _, tokenErr, err := oauthTokenRequest(ctx, client, cred, values)\n    if err == nil {\n        break\n    }\n    if tokenErr != nil && (tokenErr.Code == \"invalid_grant\" || tokenErr.Code == \"invalid_client\") {\n        return err // permanent; do not retry\n    }\n    lastErr = err\n    time.Sleep(backoff(attempt))\n}\nreturn lastErr","preventionTips":["Keep `client_id`/`client_secret`, `redirect_uri`, and `scope` aligned with the IdP registration.","Match `TokenAuthMethod` to the IdP's configured token auth style.","Capture the raw response body when debugging to decode non-standard IdP error shapes."],"tags":["oauth","mcp","network","http","identity-provider"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}