Skip to content

fix(mcp): surface real API error messages instead of 'restricted or blocked' - #1406

Open
Dhravya wants to merge 2 commits into
mainfrom
fix/mcp-403-error-message
Open

fix(mcp): surface real API error messages instead of 'restricted or blocked'#1406
Dhravya wants to merge 2 commits into
mainfrom
fix/mcp-403-error-message

Conversation

@Dhravya

@Dhravya Dhravya commented Aug 3, 2026

Copy link
Copy Markdown
Member

Why?

Plain T-1554: a user with a read-only MCP OAuth grant got 403s on memory listing, and the client rendered them as "Access forbidden. Your account may be restricted or blocked." The API's actual error body said {"error": "This API key has read-only access"} — but handleError discarded it, so the user (and support) chased a nonexistent account ban.

Two masking layers:

  1. handleError used the raw error message, which for our raw-fetch endpoints was a hardcoded string ("Failed to fetch documents") or unparsed JSON, and fell back to the scary "restricted or blocked" text when empty.
  2. getDocuments didn't read the response body at all.

What?

  • New extractApiErrorMessage() unwraps JSON error bodies ({"error": ...} / {"message": ...}) so the API's real reason reaches the user.
  • getDocuments and listMemoryEntries now pass the (unwrapped) response body through with the status, letting handleError apply status-aware fallbacks when the body is empty.
  • Reworded the empty-body 403 fallback to point at the common cause first: "Access forbidden. This connection may be read-only or scoped to specific spaces — reconnect with broader access, or check your account status."

Companion API-side fix (read-only grants couldn't call semantically-read POST list endpoints at all): supermemoryai/mono#2772.

Testing

  • Added tests: a 403 with a JSON error body surfaces the API's message; an empty-body 403 gets the scope-aware fallback. vitest run src/server/client/index.test.ts — 3 passed.
  • tsc --noEmit -p tsconfig.json clean. (The check-types script also runs tsconfig.widget.json, which fails on origin/main with a pre-existing UseAppOptions.strict error, unrelated.)

🤖 Generated with Claude Code


Note

Low Risk
User-facing error text only; no changes to auth, tokens, or request semantics beyond reading error response bodies.

Overview
MCP client errors now show the API’s real reason instead of generic “restricted or blocked” text or hardcoded fetch failures.

Adds extractApiErrorMessage() to parse JSON bodies (error / message) and use that string in thrown errors. getDocuments and listMemoryEntries read the response body on non-OK responses and pass the unwrapped message (with status) into handleError, which applies the same parsing and uses a read-only / scoped-connection fallback when a 403 has no body.

Tests cover a 403 with {"error":"This API key has read-only access"} and an empty-body 403 matching the new fallback.

Reviewed by Cursor Bugbot for commit 88bb89e. Bugbot is set up for automated code reviews on this repo. Configure here.

…locked'

The client swallowed the API's 403 error body and showed a generic
'Access forbidden. Your account may be restricted or blocked.' — which
reads like an account ban. In T-1554 the actual cause was a read-only
OAuth grant ('This API key has read-only access'), but the user couldn't
tell.

- Unwrap JSON error bodies ({"error": ...}) in handleError and in the
  raw-fetch list endpoints so the API's message reaches the user
- Reword the empty-body 403 fallback to point at read-only/scoped
  connections before suggesting account restrictions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
supermemory-mcp b0185ac Aug 03 2026, 10:36 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 3, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
supermemory-app b0185ac Commit Preview URL

Branch Preview URL
Aug 03 2026, 10:38 PM

@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown

Claude finished @Dhravya's task in 1m 45s —— View job


Code Review Complete

  • Analyzing PR context
  • Getting PR diff and existing comments
  • Reading changed files and surrounding code
  • Reviewing for bugs, security issues, and logic errors
  • Submitting review

Summary: This PR correctly fixes the error message handling for 403 responses. The extractApiErrorMessage function safely unwraps JSON error bodies, and the new scope-aware fallback message guides users toward actual causes (read-only grants, scoped access) instead of suggesting account issues.

Score: 9/10 — No bugs or security issues found. Good to ship.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 88bb89e. Configure here.

const message = extractApiErrorMessage(await response.text())
throw Object.assign(new Error(message ?? ""), {
status: response.status,
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty message on unhandled 4xx

Low Severity

Non-OK fetch paths now throw with message ?? "" instead of a local fallback like "Failed to fetch memory entries". handleError only maps specific status codes; for other 4xx with an empty body, it rethrows the original Error, so the user can see a blank message instead of any text.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 88bb89e. Configure here.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview: Fixes error message handling in the MCP client to surface API error messages (especially read-only grant 403s) instead of a generic "restricted or blocked" fallback.

Issues found: None — this looks good to ship.

The extractApiErrorMessage function safely unwraps JSON error bodies with proper fallback to raw text on parse failure. The 403 case now has a scope-aware fallback message that correctly guides users toward the actual cause (read-only grants, scoped access) rather than suggesting account issues.

Regarding the Cursor Bugbot observation about empty messages on unhandled 4xx status codes: this is technically accurate but low-impact since the common status codes (400, 401, 402, 403, 404, 422, 429, 500+) all have explicit fallbacks in handleError. Unusual status codes like 418 or 451 would surface an empty message, but these are rare enough that this isn't a blocking concern for the targeted fix.

Tests cover the key scenarios: JSON body extraction and empty-body fallback.

Score: 9/10

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant