[Bug] Voice mode WebSocket connection blocked by Cloudflare challenge on claude.ai
Bug Description
Voice mode fails with error: "Voice stream error: Connection closed: code 1002 — Expected 101 status code"
Root cause: Cloudflare managed challenge on claude.ai blocks the direct WebSocket connection from Claude Code CLI.
When Claude Code tries to connect to wss://claude.ai/api/ws/speech_to_text/voice_stream,
Cloudflare returns HTTP 403 instead of 101 (WebSocket upgrade), because the CLI client
cannot pass the Cloudflare JS challenge and doesn't have a cf_clearance cookie.
Voice works perfectly fine in the claude.ai web app — the browser passes the Cloudflare
challenge, gets the cf_clearance cookie, and the WebSocket upgrade succeeds.
Environment:
- Claude Code version: 2.1.66 (Homebrew cask, macOS)
- Subscription: Max
- Auth: OAuth (logged in via claude login)
- Network: EU (Germany) IP, no VPN, no corporate proxy
- curl https://claude.ai returns 403 with cf-mitigated: challenge
Steps to reproduce:
- /voice to enable voice mode
- Hold Space to record
- Error appears immediately: "Voice stream error: Connection closed: code 1002"
Expected: WebSocket connects to claude.ai speech_to_text endpoint successfully
Actual: Cloudflare blocks the connection with 403 (managed challenge)
Environment Info
- Platform: darwin
- Terminal: intellij
- Version: 2.1.66
- Feedback ID: bef172ff-03ff-46d1-84e0-c57a2461ea4e
Errors
[{"error":"Error: 1P event logging: 67 events failed to export (status=429, code=ERR_BAD_REQUEST, Request failed with status code 429)\n at queueFailedEvents (/$bunfs/root/claude:6264:2654)\n at async doExport (/$bunfs/root/claude:6264:1561)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-03-04T08:32:15.508Z"},{"error":"Error: {\"message\":\"Failed to export 67 events (status=429, code=ERR_BAD_REQUEST, Request failed with status code 429)\",\"originalLine\":\"6264\",\"originalColumn\":\"1713\",\"line\":\"6264\",\"column\":\"1713\",\"sourceURL\":\"/$bunfs/root/claude\",\"stack\":\"Error: Failed to export 67 events (status=429, code=ERR_BAD_REQUEST, Request failed with status code 429)\\n at doExport (/$bunfs/root/claude:6264:1713)\\n at processTicksAndRejections (native:7:39)\",\"name\":\"Error\"}\n at error (/$bunfs/root/claude:2402:25029)\n at <anonymous> (/$bunfs/root/claude:119:92465)\n at A4D (/$bunfs/root/claude:119:93009)\n at <anonymous> (/$bunfs/root/claude:120:17366)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-03-04T08:32:15.508Z"},{"error":"Error: 1P event logging: 4 events failed to export (status=429, code=ERR_BAD_REQUEST, Request failed with status code 429)\n at queueFailedEvents (/$bunfs/root/claude:6264:2654)\n at async doExport (/$bunfs/root/claude:6264:1561)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-03-04T08:32:26.635Z"},{"error":"Error: {\"message\":\"Failed to export 4 events (status=429, code=ERR_BAD_REQUEST, Request failed with status code 429)\",\"originalLine\":\"6264\",\"originalColumn\":\"1713\",\"line\":\"6264\",\"column\":\"1713\",\"sourceURL\":\"/$bunfs/root/claude\",\"stack\":\"Error: Failed to export 4 events (status=429, code=ERR_BAD_REQUEST, Request failed with status code 429)\\n at doExport (/$bunfs/root/claude:6264:1713)\\n at processTicksAndRejections (native:7:39)\",\"name\":\"Error\"}\n at error (/$bunfs/root/claude:2402:25029)\n at <anonymous> (/$bunfs/root/claude:119:92465)\n at A4D (/$bunfs/root/claude:119:93009)\n at <anonymous> (…
Note: Content was truncated.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗