[BUG] Remote Control SSE channel (worker/events/stream) ignores NODE_EXTRA_CA_CERTS while the API channel honors it — same process, v2.1.201 (it's Node, not Bun)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
The regular API path (chat) validates fine through the proxy, but the Remote-Control SSE transport loops forever on a self-signed-cert error. Both run in the same process, both go through the same proxy, both should use the same CA material — but only the API path picks up the configured extra CA.
What Should Happen?
The SSE fetch() should trust the same CA material as the API client. A user who sets NODE_EXTRA_CA_CERTS (exactly as the client's own error message instructs) should get a working Remote Control SSE channel. API-channel and SSE-channel TLS trust must be identical within the same process.
Error Messages/Logs
SSETransport: Opening https://api.anthropic.com/v1/code/sessions/<id>/worker/events/stream
SSETransport: Connection error: self signed certificate in certificate chain
SSETransport: Reconnecting in 30588ms (attempt 35, 885s elapsed)
[remote-bridge] Sending 1 message(s) <- outbound still works
Steps to Reproduce
- Put the client behind a TLS-inspecting proxy (Zscaler/Netskope/self-hosted) that re-signs api.anthropic.com with an internal Root CA installed in the OS store.
- Confirm the API path works — normal chat/completions succeed through the proxy.
- Enable Remote Control → the SSE channel loops on self signed certificate in certificate chain.
- Set NODE_EXTRA_CA_CERTS to a PEM containing the Root CA (verify with openssl verify), fully restart the client. Confirm it is in the process env (echo $NODE_EXTRA_CA_CERTS). API keeps working, SSE keeps failing.
Claude Model
None
Is this a regression?
No, this never worked
Last Working Version
_No response_
Claude Code Version
2.1.201
Platform
Other
Operating System
Windows
Terminal/Shell
VS Code integrated terminal
Additional Information
It's Node, not Bun — binary evidence
Previous corporate-proxy issues were dismissed on the assumption of an unfixable "Bun isolated networking stack." The binary is Node.js, verified by string/symbol analysis of the native SEA:
setGlobalDispatcher(undici),setDefaultCACertificates,getCACertificates— all Node APIs.- CA material comes from a single
getCACertificates(type = "default")call (not"system"; the--use-system-castring exists but isn't used here). - The SSE transport uses a bare
fetch()with nodispatcher/agent/ca:
// SSETransport.open()
let o = await fetch(t.href, { headers: r, signal: this.abortController.signal });
// grep for `dispatcher:` in any fetch option across the whole binary → 0 hits
- The binary even ships the error string advising "set NODE_EXTRA_CA_CERTS to your CA bundle path" — which does not fix the SSE path.
win32-x64 vs linux-x64 — identical code, only runtime differs
Same greps against both binaries of release 2.1.201:
| Probe | win32-x64 | linux-x64 |
|---|---|---|
| getCACertificates(type) | "default" | "default" |
| use-system-ca / "system" / "bundled" / "extra" | 8 / 490 / 37 / 2 | 8 / 490 / 37 / 2 |
| setGlobalDispatcher / setDefaultCACertificates | 4 / 2 | 4 / 2 |
| SSETransport | bare fetch() | bare fetch() |
| dispatcher: in fetch opts | none | none |
The Linux client's SSE works only because NODE_EXTRA_CA_CERTS is in the OS env at bootstrap (login shell), so it lands in the getCACertificates("default") snapshot before any TLS context is built. On Windows the SSE path does not pick it up even when the variable is in the process env.
Likely cause (for maintainers to confirm)
The global undici dispatcher is constructed from a CA snapshot taken before the extra CA is applied, or the SSE fetch bypasses the dispatcher that carries the extra CA (the API/SDK path uses an explicit httpsAgent/ca; the SSE path uses bare global fetch). Either way: API-channel TLS trust ≠ SSE-channel TLS trust, in the same process.
Suggested fixes
- Route the SSE
fetch()through the same dispatcher/Agent as the API client (built with the resolved CA including the"extra"set). - (Re)apply
setDefaultCACertificates(getCACertificates("default"))before the SSE transport initializes, and re-readNODE_EXTRA_CA_CERTSwhen constructing the SSE dispatcher — so late-injected env (VS Code settings, Windows user env) is honored, matching the API path. - Optionally honor
--use-system-ca/ the OS trust store for the SSE path. - Re-enable a diagnostic surface —
NODE_DEBUG=tlsis dead in the SEA build.
Related issues — same class, all dismissed
- https://github.com/anthropics/claude-code/issues/25084 — Zscaler / Windows,
SELF_SIGNED_CERT_IN_CHAIN. Closed: not planned (bug+has repro+stale, no maintainer response). - https://github.com/anthropics/claude-code/issues/22512 —
NODE_EXTRA_CA_CERTSinsettings.jsonnot respected. Closed: not planned (has repro+stale). - https://github.com/anthropics/claude-code/issues/24916 — Mac code pane fails while chat works; subprocess doesn't inherit the CA. Closed: duplicate.
- https://github.com/anthropics/claude-code/issues/24470 — Cowork ignores
NODE_EXTRA_CA_CERTS/--use-openssl-ca. - https://github.com/anthropics/claude-code/issues/26897 —
NODE_EXTRA_CA_CERTSnot working. - https://github.com/anthropics/claude-code/issues/71581 — native Windows build fails validation;
NODE_USE_SYSTEM_CA/NODE_EXTRA_CA_CERTS/BUN_CA_BUNDLEall ignored (system Node validates the same chain fine).
Why this is not a duplicate: those report the general "NODE_EXTRA_CA_CERTS ignored" symptom and were dismissed (several assuming an unfixable "Bun" stack). This report adds (a) the same-process API-vs-SSE divergence with the variable proven present, (b) binary evidence the runtime is Node, not Bun, and (c) it reproduces on the current 2.1.201 — a live, addressable Node code-path defect, not an inherent runtime limitation.
Workaround
Exempt api.anthropic.com from TLS inspection for the client IP (don't rewrite it to the MITM gateway). The SSE channel then validates against the genuine Anthropic leaf via the bundled Mozilla roots.