Summary
Removing a variable from the settings.json env map never takes effect for sessions hosted by the background daemon (Agents View chats, background jobs): env injection is additive over inherited process env, and daemon-spawned session hosts inherit the daemon's env snapshot from whenever the daemon started — in my case 28+ hours earlier. Because the four cross-session-messaging gate variables are documented as "any non-empty value, including 0/false, still disables; unset to re-enable" (CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC, DISABLE_TELEMETRY), there is no settings-level value that can neutralize the stale inherited var — the documented remediation in cross-session messaging → Availability ("Unset whichever applies") is unachievable for daemon-hosted sessions. The observable result is availability silently diverging on one machine: plain claude sessions get /list-agents, a Peer address row, and a bound inbox socket, while every Agents View chat started the same minute from the same terminal doesn't — with no error, no diagnostic, and nothing pointing at the daemon.
Environment
- Claude Code v2.1.224, npm global install under mise node 24.12.0 (
/home/will/.local/share/mise/installs/node/24.12.0/lib/node_modules/@anthropic-ai/claude-code)
- WSL2 Ubuntu, kernel 6.6.114.1-microsoft-standard-WSL2
- Daemon: transient, started on demand by
claude agents (per claude daemon status), uptime 127,569 s (~35 h) at diagnosis, held open by 11 bg workers
Repro steps
- Have
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1" (any marker var works, but this one shows the messaging impact) in ~/.claude/settings.json → env.
- Run
claude agents once so the transient daemon spawns (claude daemon status → origin: transient). Leave a background session running so the daemon stays alive.
- Remove the variable from settings.json
env.
- Open a new terminal (shell env clean; nothing else sets the var):
claude -p 'run printenv CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC and printenv CLAUDE_CODE_MESSAGING_SOCKET' → var unset, inbox socket bound (/run/user/1001/cc-socks/<pid>.sock), ListAgents tool present. Also true with --dangerously-skip-permissions.
- From that same fresh terminal,
claude agents → start a new chat → attach:
- the session host is a pre-warmed
claude bg-spare/claude bg-pty-host forked by the day-old daemon; its /proc/<pid>/environ still contains the removed var; no inbox socket binds; /list-agents is not recognized; /status shows no Peer address row.
claude daemon stop, relaunch claude agents → new daemon inherits the clean env → new chats bind sockets and /list-agents works. (Step 6 is the workaround; the daemon-respawn-on-demand behavior is per claude daemon --help.)
Evidence collected
/proc/<pid>/environ scan of every live claude process while reproducing (flag = removed var still present):
pid=934405 flag=False claude agents --dangerously-skip-permissions (fresh front-end, clean)
pid=1560142 flag=False claude agents --dangerously-skip-permissions (fresh front-end, clean)
pid=1560210 flag=True claude bg-pty-host ... /tmp/cc-daemon-1001/e6fb62ab/spare/4486810d.pty.sock
pid=1560299 flag=True claude bg-spare ... spare/4486810d.claim.sock (forked 9h AFTER the settings change)
pid=1590636 flag=True claude bg-pty-host ... spare/c804d0c5.pty.sock (forked 11h AFTER the settings change)
... (all 11 bg workers: flag=True)
pid=2071812 flag=True @anthropic-ai/claude-code/bin/claude daemon run ... (started Aug 7 05:07, 28h before the change)
PPID chain: every bg-spare/bg-pty-host parents directly to daemon pid 2071812. A headless probe session asked to call ListAgents returned "No reachable agents" — correct, since no other session on the machine could bind a socket.
Why this is worse than ordinary daemon-env caching
- Additive injection: settings env injection adds/overrides vars but never unsets one that settings no longer defines, so the inherited stale var survives every new session the daemon hosts, indefinitely.
- No override value exists: for
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC and DISABLE_TELEMETRY, any non-empty value disables (docs: "Setting it to 0 or false still disables"), so a user cannot even write a neutralizing value into settings — unset-at-source is the only documented path, and it doesn't reach these sessions.
- No diagnostic: the Availability checklist says work through version/OS/provider/env-vars — all of which check out in the user's fresh terminal. Nothing surfaces "this session's env came from a 35-hour-old daemon".
/status, claude doctor, and claude daemon status are all silent about the divergence.
Suggested fixes (ranked)
- When claiming a spare / spawning a session host, re-derive the settings
env map from a fresh settings.json read, and drop from the child env any var that an earlier settings injection added but current settings no longer defines (requires remembering which vars came from settings vs the user's shell — a small manifest in the daemon would do).
- Daemon watches settings.json env (hash check on spawn is enough): on drift, recycle pre-warmed spares and log it;
claude daemon status shows env: stale since <time> until then.
- Cheapest: docs + surfacing. Add to cross-session-messaging Availability and env-vars: "sessions hosted by the background daemon inherit its environment; after changing these variables run
claude daemon stop". Have /doctor (or the /list-agents unrecognized-state hint) warn when the session env contains a gate var that current settings.json does not define.
Related
Workaround for anyone hitting this
claude daemon stop (daemon respawns on demand with your current env), then relaunch claude agents. Find a suspect daemon with claude daemon status; confirm with tr '\0' '\n' < /proc/<daemon-pid>/environ | grep <var>.
Summary
Removing a variable from the settings.json
envmap never takes effect for sessions hosted by the background daemon (Agents View chats, background jobs): env injection is additive over inherited process env, and daemon-spawned session hosts inherit the daemon's env snapshot from whenever the daemon started — in my case 28+ hours earlier. Because the four cross-session-messaging gate variables are documented as "any non-empty value, including0/false, still disables; unset to re-enable" (CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,DISABLE_TELEMETRY), there is no settings-level value that can neutralize the stale inherited var — the documented remediation in cross-session messaging → Availability ("Unset whichever applies") is unachievable for daemon-hosted sessions. The observable result is availability silently diverging on one machine: plainclaudesessions get/list-agents, aPeer addressrow, and a bound inbox socket, while every Agents View chat started the same minute from the same terminal doesn't — with no error, no diagnostic, and nothing pointing at the daemon.Environment
/home/will/.local/share/mise/installs/node/24.12.0/lib/node_modules/@anthropic-ai/claude-code)claude agents(perclaude daemon status), uptime 127,569 s (~35 h) at diagnosis, held open by 11 bg workersRepro steps
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"(any marker var works, but this one shows the messaging impact) in~/.claude/settings.json→env.claude agentsonce so the transient daemon spawns (claude daemon status→origin: transient). Leave a background session running so the daemon stays alive.env.claude -p 'run printenv CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC and printenv CLAUDE_CODE_MESSAGING_SOCKET'→ var unset, inbox socket bound (/run/user/1001/cc-socks/<pid>.sock),ListAgentstool present. Also true with--dangerously-skip-permissions.claude agents→ start a new chat → attach:claude bg-spare/claude bg-pty-hostforked by the day-old daemon; its/proc/<pid>/environstill contains the removed var; no inbox socket binds;/list-agentsis not recognized;/statusshows noPeer addressrow.claude daemon stop, relaunchclaude agents→ new daemon inherits the clean env → new chats bind sockets and/list-agentsworks. (Step 6 is the workaround; the daemon-respawn-on-demand behavior is perclaude daemon --help.)Evidence collected
/proc/<pid>/environscan of every live claude process while reproducing (flag = removed var still present):PPID chain: every
bg-spare/bg-pty-hostparents directly to daemon pid 2071812. A headless probe session asked to callListAgentsreturned "No reachable agents" — correct, since no other session on the machine could bind a socket.Why this is worse than ordinary daemon-env caching
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICandDISABLE_TELEMETRY, any non-empty value disables (docs: "Setting it to0orfalsestill disables"), so a user cannot even write a neutralizing value into settings — unset-at-source is the only documented path, and it doesn't reach these sessions./status,claude doctor, andclaude daemon statusare all silent about the divergence.Suggested fixes (ranked)
envmap from a fresh settings.json read, and drop from the child env any var that an earlier settings injection added but current settings no longer defines (requires remembering which vars came from settings vs the user's shell — a small manifest in the daemon would do).claude daemon statusshowsenv: stale since <time>until then.claude daemon stop". Have/doctor(or the/list-agentsunrecognized-state hint) warn when the session env contains a gate var that current settings.json does not define.Related
DISABLE_TELEMETRYsilently gating Agent View via feature-flag coupling — same family of silent env-var gating; the maintainer noted broader killswitch coupling remained open./status-level socket/health indicator would help both./feedbackfrom the nonessential-traffic var — prior art that these kill-switches over-reach.Workaround for anyone hitting this
claude daemon stop(daemon respawns on demand with your current env), then relaunchclaude agents. Find a suspect daemon withclaude daemon status; confirm withtr '\0' '\n' < /proc/<daemon-pid>/environ | grep <var>.