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 false

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Check the embedded `resp.Status` for the exact reason (e.g. `401 Unauthorized` → wrong client auth).
  2. Verify the client authentication method and credentials are accepted at the revocation endpoint, not just the token endpoint.
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/b633a88aac98ced3. Report an issue: GitHub.