siyuan-note/siyuan · error
OAuth revocation endpoint returned
Error message
OAuth revocation endpoint returned %s
What it means
The OAuth revocation endpoint replied with a non-2xx status while revoking the refresh or access token. The HTTP status text is wrapped in the returned error and joined across both token revocation attempts; revocation failure is also logged as a warning but still returned to the caller.
Solutions
- Read resp.Status in the error and check the IdP logs for the actual rejection reason
- If the token was already revoked, treat the outcome as idempotent success on your side and ignore the joined error
- Re-fetch OAuth discovery metadata so RevocationEndpoint matches the current server
- Confirm client_id/client_secret sent with the revocation request are accepted by the IdP
Defensive patterns
Strategy: try-catch
Validate before calling
resp, err := client.Post(revocationEndpoint, "application/x-www-form-urlencoded", strings.NewReader(body))
if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
// safe to proceed
} Try / catch
err := revokeOAuthCredential(ctx, client, cred)
if err != nil {
var statusErr interface{ Error() string }
if strings.Contains(err.Error(), "OAuth revocation endpoint returned") {
// token may already be revoked; log and treat as best-effort
logging.LogWarnf("revocation non-2xx, continuing: %s", err)
}
} Prevention
- Treat revocation as idempotent — an already-revoked token often yields 400
- Re-run OAuth discovery when the IdP changes endpoints
- Confirm RFC 7009 support on the authorization server before relying on revocation
When it happens
Trigger: POST to the revocation endpoint returns 400/401/404/5xx — e.g. the token was already revoked, the client credentials are wrong for RFC 7009 revocation, or the endpoint path in server metadata is stale.
Common situations: Revoking an already-expired/revoked token (some IdPs return 400), IdP rotated its revocation path, clock/auth issues causing 401, or the IdP not implementing RFC 7009 at the advertised endpoint.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- OAuth token endpoint returned
- register OAuth client
- server returned
- asset path [ ] does not match data path [ ]
- authentication probe returned HTTP " + response.status
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/b633a88aac98ced3.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/mcp/client/oauth.go:768
}
values := url.Values{"token": {token.value}, "token_type_hint": {token.hint}}
applyOAuthClientAuthentication(values, nil, credential)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, credential.RevocationEndpoint, strings.NewReader(values.Encode()))
if err != nil {
result = errors.Join(result, err)
continue
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
applyOAuthClientAuthentication(nil, req, credential)
resp, err := client.Do(req)
if err != nil {
result = errors.Join(result, err)
continue
}
io.Copy(io.Discard, io.LimitReader(resp.Body, 1<<20))
resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
result = errors.Join(result, fmt.Errorf("OAuth revocation endpoint returned %s", resp.Status))
}
}
if result != nil {
logging.LogWarnf("mcp oauth: revoke credentials failed: %s", result)
}
return result
}
func isSecureOAuthEndpoint(endpoint string) bool {
parsed, err := url.Parse(endpoint)
if err != nil {
return false
}
if parsed.Scheme == "https" {
return true
}
if parsed.Scheme != "http" {
return falseView on GitHub (pinned to 9f775e8a12)