siyuan-note/siyuan · warning
OAuth revocation endpoint returned %s
Error message
OAuth revocation endpoint returned %s
What it means
The OAuth revocation endpoint returned a non-2xx HTTP status for at least one of the tokens being revoked. Revocation runs in a best-effort goroutine during `DisconnectMCPOAuth`; the local credential is already removed, so this error only affects whether the IdP also invalidates the token server-side.
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 251596fc0d)
Solutions
- Check the embedded `resp.Status` for the exact reason (e.g. `401 Unauthorized` → wrong client auth).
- Verify the client authentication method and credentials are accepted at the revocation endpoint, not just the token endpoint.
- Since local state is already cleared, this is non-fatal; the token will simply expire naturally at the IdP if revocation keeps failing.
Defensive patterns
Strategy: fallback
Try / catch
// Revocation is best-effort: log and continue; local credentials are already gone.
if err := revokeOAuthCredential(ctx, client, cred); err != nil {
logging.LogWarnf("revoke failed (non-fatal): %s", err)
} Prevention
- Treat revocation failure as non-fatal; local state is already cleared.
- Verify the revocation endpoint accepts the same client auth method as the token endpoint.
- Let tokens expire naturally at the IdP if revocation keeps failing.
When it happens
Trigger: `revokeOAuthCredential` POSTs the access and/or refresh token to `RevocationEndpoint` and receives `statusCode < 200 || >= 300`; the error is joined into `result` via `errors.Join`.
Common situations: The token was already expired or revoked by the IdP. The `client_secret_basic`/`client_secret_post` auth method at the revocation endpoint differs from the token endpoint. Transient IdP outage during disconnect.
Related errors
- OAuth token endpoint returned %s
- discover OAuth authorization server: %w
- register OAuth client: %w
- OAuth protected resource metadata not found
- download custom emoji failed with status %d
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/b633a88aac98ced3.
Report an issue: GitHub.