siyuan-note/siyuan · error

server returned %s

Error message

server returned %s

What it means

Returned by mcpOAuthHandler.Authorize when the response is HTTP 403, the Bearer challenge carried an 'error' parameter that is NOT 'insufficient_scope', and thus cannot be resolved by re-authenticating the user. The status text is embedded via %s. The rationale is that 403 with insufficient_scope is retryable via token refresh, but other 403s (e.g. invalid_token, insufficient_user_privileges) are terminal for this credential.

Source

Thrown at kernel/mcp/client/oauth.go:196

		RefreshToken: credential.RefreshToken,
		Expiry:       credential.Expiry,
	}
}

func (h *mcpOAuthHandler) Authorize(ctx context.Context, req *http.Request, resp *http.Response) (retErr error) {
	defer resp.Body.Close()
	defer io.Copy(io.Discard, io.LimitReader(resp.Body, 1<<20))

	challenges, err := oauthex.ParseWWWAuthenticate(resp.Header.Values("WWW-Authenticate"))
	if err != nil {
		return fmt.Errorf("parse OAuth challenge: %w", err)
	}
	if !hasBearerChallenge(challenges) {
		return fmt.Errorf("server returned %s without an OAuth Bearer challenge", resp.Status)
	}
	challengeError := bearerChallengeParam(challenges, "error")
	if resp.StatusCode == http.StatusForbidden && challengeError != "insufficient_scope" {
		return fmt.Errorf("server returned %s", resp.Status)
	}
	interactive := h.interactive.Load()
	if interactive {
		defer func() {
			if retErr != nil && !errors.Is(retErr, context.Canceled) {
				setMCPRuntimeStateForContext(ctx, h.server.ID, "authorization_required", 0, retErr.Error(), "")
			}
		}()
	}

	prm, err := discoverProtectedResource(ctx, challenges, req.URL.String(), h.client)
	if err != nil {
		return err
	}

	asm, err := auth.GetAuthServerMetadata(ctx, prm.AuthorizationServers[0], h.client)
	if err != nil {
		return fmt.Errorf("discover OAuth authorization server: %w", err)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Distinguish the challenge 'error' value: for invalid_token / revoked tokens, clear the stored credential and re-run the interactive OAuth flow to obtain a fresh token.
  2. For insufficient_user_privileges or policy denials, the account itself is not authorized — request access from the MCP server operator; no client-side refresh will help.
  3. If the server is misclassifying an insufficient_scope condition as a different error, report it upstream so the retry path can engage.
Defensive patterns

Strategy: try-catch

Try / catch

// Terminal 403: do not retry OAuth; surface to user / audit account permissions.
if strings.Contains(err.Error(), "server returned 403") {
    // stop retry, request operator review of account permissions
}

Prevention

When it happens

Trigger: MCP server returns 401 with a Bearer challenge whose 'error' param is set and not insufficient_scope, AND the response status (after re-evaluation) is 403 — the guard checks resp.StatusCode == http.StatusForbidden && challengeError != "insufficient_scope".

Common situations: User's account lacks the required role/permission (insufficient_user_privileges); token revoked server-side (invalid_token) but reported as 403; access control policy denies the principal entirely.

Related errors


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