[BUG] Desktop Code tab: NODE_USE_SYSTEM_CA=1 in sandboxed Bash makes every Node TLS init print "failed to copy trust settings" errors; settings env can't override it
Environment
- Claude desktop app 2.26454.2, Code tab, local session; bundled Claude Code 2.1.293 (
CLAUDE_CODE_ENTRYPOINT=claude-desktop) - macOS 26.2 (25C56), arm64
- Bash tool sandbox enabled (Seatbelt);
sandbox.enableWeakerNetworkIsolationnot set - zsh 5.9, Node v26.8.1 (nvm), pnpm 11.28.2
NODE_USE_SYSTEM_CA,NODE_EXTRA_CA_CERTSandNODE_OPTIONSare not set in my shell profiles,nvm.sh,launchctl getenvor LaunchAgents
What happens
Every Bash command in a Code-tab session, including subagents' commands, inherits NODE_USE_SYSTEM_CA=1. With that set, Node reads the macOS keychain whenever it builds its default TLS context. Inside the sandbox it cannot read the certificates' trust settings, so it writes one line to stderr per failed lookup:
ERROR: failed to copy trust settings of system certificate-25291
-25291 is the OSStatus errSecNotAvailable, which Node's fprintf appends with no separator (src/crypto/crypto_context.cc, IsCertificateTrustedForPolicy). Node checks the User and Admin trust domains for every certificate in the keychain search list, so the count is 2 × the number of certificates. Here that is 58 lines (29 certificates: 26 in the login keychain, 3 in System.keychain).
The variable gives nothing in return inside the sandbox: because every lookup fails, Node trusts zero system certificates (tls.getCACertificates('system').length is 0 inside the sandbox and 8 outside).
Any Node tool that sets up TLS adds these lines. pnpm 11 does so at startup, so every pnpm --version, pnpm --filter <pkg> typecheck, pnpm test, etc. adds 58 lines to the agent's context. With many pnpm calls across subagents, this reaches thousands of lines per session. (node -e 1 and npm --version print nothing, since they don't set up TLS.)
Where the variable comes from
- The bundled CLI process (
~/Library/Application Support/Claude/claude-code/2.1.293/.../claude) is started withNODE_USE_SYSTEM_CA=1(ps ewwon the Bash shell's parent). The desktop app's main process does not have it. - In
Claude.app/Contents/Resources/app.asar(2.26454.2, minified), the Code-session env is built as:
``js`
sessionEnv:{...l,DISABLE_MICROCOMPACT:"1",CLAUDE_CODE_TERMINAL_MCP_TOOLS:…,NODE_USE_SYSTEM_CA:"1"}
l
As far as I can tell, is the env merged from process.env, the Local environment editor's variables and scoped variables. The literal comes last, so it overrides any value set there. (A separate CA helper in the bundle also returns NODE_USE_SYSTEM_CA:"1" when no CA bundle is primed, otherwise NODE_EXTRA_CA_CERTS`.)
- The standalone CLI (Homebrew cask 2.1.285) does not set the variable: in both CLI binaries the string only appears in Bun's own TLS code. So this seems specific to sessions the desktop app starts.
Repro (Code-tab local session, sandbox on, no workaround)
echo "[$NODE_USE_SYSTEM_CA]"
# [1]
node -e "require('node:https').get('https://example.invalid').on('error',()=>{})" 2>&1 | grep -c 'failed to copy trust settings'
# 58
NODE_USE_SYSTEM_CA=0 node -e "require('node:https').get('https://example.invalid').on('error',()=>{})" 2>&1 | grep -c 'failed to copy trust settings'
# 0
The blocked service, from a plain terminal outside Claude Code:
printf '(version 1)\n(allow default)\n(deny mach-lookup (global-name "com.apple.trustd.agent"))\n' > /tmp/deny-trustd-agent.sb
NODE_USE_SYSTEM_CA=1 sandbox-exec -f /tmp/deny-trustd-agent.sb node -e "require('node:https').get('https://example.invalid').on('error',()=>{})" 2>&1 | grep -c 'failed to copy trust settings'
# 58 (0 without the deny rule)
Denying only com.apple.trustd.agent reproduces the same 58 lines and the empty system store. The profile that 2.1.293 generates allows that service only when sandbox.enableWeakerNetworkIsolation is true.
What I tried
"env": { "NODE_USE_SYSTEM_CA": "0" }in~/.claude/settings.json, then fully quitting and reopening the app: no effect. This matches the settings reference: when the desktop app starts the session, its launch environment wins over a settingsenvvalue for the same variable.- The Local environment editor from the desktop docs (environment dropdown → Local → gear icon): I could not find it in my build. Going by the code above, its value would be overridden anyway.
CLAUDE_CODE_CERT_STOREdoes not help: it only selects the CA sources Claude Code itself trusts, and the CLI never sets or clearsNODE_USE_SYSTEM_CAfor subprocesses.
Workaround (Bash tool only; main agent and subagents both drop to 0 lines in new sessions)
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "[ -n \"$CLAUDE_ENV_FILE\" ] && echo 'export NODE_USE_SYSTEM_CA=0' >> \"$CLAUDE_ENV_FILE\"; exit 0"
}
]
}
]
}
Going by the sandbox-exec result, sandbox.enableWeakerNetworkIsolation: true should also stop the errors. I have not tried it in a live session, and the docs say it reduces security.
Expected
Any one of these would fix it:
- Don't pass
NODE_USE_SYSTEM_CA=1to sandboxed Bash commands. Inside the sandbox it cannot work (0 system certificates) and only adds noise. - Let a user setting (settings
envor the Local environment editor) override it. Today the literal is applied last. - If Node tools should trust OS-installed CAs inside the sandbox, allow
com.apple.trustd.agentfor that purpose without tying it toenableWeakerNetworkIsolation.
Related
- #34876 (closed as not planned) and #82793 (open): the macOS sandbox denies the Mach lookup that Go needs for TLS verification, and #82793 shows the service is
com.apple.trustd.agent. Thesandbox-exectest above shows this Node error comes from the same denial. - pmark/arcadia#890: another project reports the same
failed to copy trust settings of system certificate-25291lines (~16 per call) on everymise exec -- node ...call inside the Claude Code sandbox. That report does not identify a root cause.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>