Settings env removals never propagate to daemon-hosted sessions (Agents View/background): stale inherited env silently disables cross-session messaging, and no settings value can neutralize it (v2.1.224)
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(perclaude 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 agentsonce 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),ListAgentstool 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-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>/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_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. - 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, andclaude daemon statusare all silent about the divergence.
Suggested fixes (ranked)
- When claiming a spare / spawning a session host, re-derive the settings
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). - Daemon watches settings.json env (hash check on spawn is enough): on drift, recycle pre-warmed spares and log it;
claude daemon statusshowsenv: 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-agentsunrecognized-state hint) warn when the session env contains a gate var that current settings.json does not define.
Related
- #58383 (closed, fixed v2.1.140):
DISABLE_TELEMETRYsilently gating Agent View via feature-flag coupling — same family of silent env-var gating; the maintainer noted broader killswitch coupling remained open. - #84945 (open): inbox socket silently fails to bind with no surfaced error — different mechanism, same undiagnosable end state; a
/status-level socket/health indicator would help both. - #52787 (closed): decoupling
/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 relaunch claude agents. Find a suspect daemon with claude daemon status; confirm with tr '\0' '\n' < /proc/<daemon-pid>/environ | grep <var>.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗