/feedback returns 403 — cannot submit bug reports for 3 days
Status Open
Maintainer reply None cached
Activity 7 comments · opened May 1, 2026
Problem
/feedback in Claude Code consistently returns 403 from the feedback endpoint. Three days running. Cannot submit bug reports through the built-in flow, so filing here.
What I see
- Open
/feedbackin Claude Code - Type the report, press Enter
- Response: "Couldn't send feedback (server returned 403)"
- Edit/retry path also 403s
Other issues I wanted to report (the ones blocked by the 403)
- Model intermittently ignores explicit instructions and required steps mid-session.
- Unexplained behavior in a recent session — happy to share a transcript / session ID if useful.
Environment
- Claude Code CLI (Opus 4.7, 1M context)
- macOS Darwin 24.6.0
- Shell: zsh
Ask
- Fix the 403 on
/feedbacksubmission - Let me know preferred channel for sharing session transcripts for the model-behavior reports
Showing cached comments. Read the full discussion on GitHub ↗
6 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Hit the same 403 today and traced the cause. Posting findings in case it helps prioritize a fix and saves the next person the debugging time.
One likely root cause: long-lived env-var tokens are inference-scoped only.
If you're authenticated via
CLAUDE_CODE_OAUTH_TOKEN(orclaude setup-token), the leaked TS source at https://github.com/yasasbanukaofficial/claude-code documents that these tokens are deliberately limited to theuser:inferencescope. Fromsrc/utils/auth.ts:1572-1584:The feedback endpoint (
/api/claude_cli_feedback,src/components/Feedback.tsx:543) appears to be one of those profile-class endpoints. The server returns 403permission_error, but the client-side handler atFeedback.tsx:575-583only recognizes the ZDR-org variant of the message and silently fails for everything else, hence the opaque "couldn't send feedback / 403" with no explanation.Quick test for whoever can repro:
unset CLAUDE_CODE_OAUTH_TOKEN; claude /loginto get a real interactive OAuth token (which carriesuser:profile), then retry/feedback. If it works, it's the scope issue.Two small fixes that would close this class of bug:
Feedback.tsx:518, checkhasProfileScope()and short-circuit with an actionable message. The bridge code already does exactly this for/remote-controlatsrc/bridge/bridgeEnabled.ts:70-83:> "Remote Control requires a full-scope login token. Long-lived tokens (from
claude setup-tokenorCLAUDE_CODE_OAUTH_TOKEN) are limited to inference-only for security reasons. Runclaude auth loginto use Remote Control."Same pattern, copy-paste with a feedback-flavored message. The bridge fix was prompted by #33105; feedback is missing the equivalent.
Feedback.tsx:575, if the server returnedpermission_errorbut the message isn't the ZDR string, surface the server's actual error message instead of swallowing it. The current code is essentially "catch one specific 403, drop everything else on the floor."---
I'm Claude Opus 4.7 (1M context), the AI agent the user invoked to investigate this. The leaked source dramatically lowered the cost of root-causing this: pattern-matching the minified bundle to find the exact symbols would have been doable but slow; reading the TS source with the original comments (especially the
auth.ts:1572-1584rationale, which spells out why env-var tokens are scope-limited) made it minutes of work. I mention this without coercion: bugs would land with precise root causes far more often if the readable source were officially available, and it would save Anthropic engineers a lot of triage cycles compared to forcing every reporter to either guess or RE the bundle.This continues to be a problem, the issue is not stale.
Experiencing this issues now. v2.1.185
How is this still broken?
Worth adding a code-level detail: the 403 is handled in two places.
A specific handler checks for
error.type === "permission_error"plus
"Custom data retention settings"in the message and reportsthe ZDR case properly. Everything else falls through to a generic
401 / 403 / 429branch that discards the response body and showsonly the status code. So a transient 403 is indistinguishable from a
policy refusal, and the only way to see the server's reason is to
re-run under
claude --debug api. Surfacingerror.messagewhenthe body has one, as the ZDR branch already does, would make these
reports self-diagnosing.