Voice mode: WebSocket blocked by Cloudflare bot challenge (403 cf-mitigated)
Summary
Voice mode (/voice) fails with "Voice connection failed. Check your network and try again." The root cause is Cloudflare blocking the Node.js WebSocket upgrade to wss://claude.ai/api/ws/speech_to_text/voice_stream with a 403 cf-mitigated: challenge response.
Environment
- Claude Code: v2.1.74
- Plan: Claude Max (OAuth)
- OS: macOS 15 (Darwin 25.2.0), Apple Silicon
- Node.js: v22.18.0
- Region: Brazil (Cloudflare PoP: GRU — São Paulo)
- Terminal: Apple Terminal
Steps to Reproduce
- Enable voice mode with
/voice - Hold Space to record
- Error appears in bottom-right:
Voice connection failed. Check your network and try again.
Diagnosis
All prerequisites pass:
- ✅ OAuth token valid (
claudeAiOauth.accessTokenpresent, not expired) - ✅ Feature gates enabled (
tengu_amber_quartz✅) - ✅ Audio dependencies installed (
ffmpeg,sox,rec) - ✅ Microphone permissions granted (Terminal.app authorized in TCC)
- ✅ Audio recording works (
ffmpeg -f avfoundationrecords successfully) - ✅ Network connectivity to
claude.aiworks (HTTPS 200 from Node.js)
The WebSocket connection is blocked by Cloudflare:
// Node.js ws library — FAILS
const ws = new WebSocket('wss://claude.ai/api/ws/speech_to_text/voice_stream?...');
// → 403 with header: cf-mitigated: challenge, server: cloudflare
# Python websockets — SUCCEEDS (different TLS fingerprint)
async with websockets.connect(uri, additional_headers=headers) as ws:
# → Connected OK, KeepAlive works
The cf-mitigated: challenge header confirms Cloudflare's bot detection is fingerprinting the TLS/HTTP characteristics of Node.js and triggering a JavaScript challenge, which a WebSocket client cannot solve.
Cloudflare Ray IDs
9dbd0560eb9c5f28(HTTPS test)9dbd12adcb68c27c(WebSocket test — blocked)
Both routed through GRU (São Paulo). This may be region-specific — Cloudflare WAF rules can differ by PoP.
Expected Behavior
The voice WebSocket should connect successfully, as the OAuth token is valid and the endpoint is reachable.
Suggested Fix
Adjust Cloudflare WAF/bot rules to allowlist WebSocket upgrade requests to /api/ws/speech_to_text/voice_stream from Claude Code's User-Agent (claude-code/*), or configure a Cloudflare bypass rule for this endpoint.
4 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Same issue on Windows. Confirmed the WebSocket returns 403 cf-mitigated: challenge from Cloudflare — tested directly with Node.js.
System info:
Diagnosis:
This is purely a server-side Cloudflare WAF issue. No client-side workaround possible. Needs Anthropic to allowlist WebSocket upgrade requests from the CLI User-Agent on this endpoint.
+1 — same issue on Linux.
Network capture confirms TCP+TLS to
34.149.66.137:443(GCP) succeeds — connection reaches ESTAB but fails at application layer. Error is purely Cloudflare WebSocket rejection.This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.