[BUG] claude-vscode leaks one `dbus-daemon --session` per Bash tool call and per Task subagent on WSL2 (5620 orphans in 5 days)
Summary
On WSL2, the Claude Code VSCode extension (CLAUDE_CODE_ENTRYPOINT=claude-vscode) leaks a dbus-daemon --session process every time a Bash tool call or Task (subagent) tool call is invoked. The daemon outlives the originating shell, gets reparented to WSL's /init (the WSL equivalent of PID 1), and accumulates indefinitely.
After ~5 days of normal use I had 5620 orphan dbus-daemon --session processes, all spawned in a Claude Code context, none with any child processes.
This is related to but distinct from #26987 (tab-close leaks): the rate (~46/hour) and the env evidence below show that per-tool-call shell creation is the primary driver, not tab opens. It also overlaps with #19045 / #20369 / #23154 / #33947 (subagent + MCP cleanup), but the leaked process here is an unrelated side-effect daemon (dbus-daemon), not the Claude/MCP child itself.
Environment
- OS: Windows 11 + WSL2
- Distro: Ubuntu (kernel 6.6.87.2-microsoft-standard-WSL2)
- Shell: zsh
- Claude Code via VSCode extension (Remote-WSL);
CLAUDE_AGENT_SDK_VERSION=0.2.126 - VSCode server build
10c8e557c8b9f9ed0a87f61f1c9a44bde731c409 - No systemd in WSL (default)
Reproducer
- Use Claude Code via the VSCode extension on WSL2 normally for a few hours — invoke Bash tool calls and Task subagents as part of regular work.
pgrep -af 'dbus-daemon.*--session' | wc -l— count grows monotonically.ps -eo pid,ppid,etime,cmd | awk '/[d]bus-daemon.*--session/ && $2 == 1'(substitute the PID of/initon WSL2; mine was 5104) — these are the orphans.
Evidence it's Claude Code
For each leaked daemon I dumped /proc/<pid>/environ. Counts:
| Env var | Count |
| --- | --- |
| CLAUDE_CODE_ENTRYPOINT=claude-vscode | 5620 / 5620 |
| CLAUDECODE=1 | 5619 / 5620 |
| CLAUDE_AGENT_SDK_VERSION=0.2.126 | 3132 / 5620 |
Reading:
- 100% were forked in a
claude-vscodecontext. No false positives from other tools. - ~56% carry the Agent SDK version env, so they came from Task (subagent) shells.
- The remaining ~44% came from non-SDK contexts — main-agent Bash tool calls and the main interactive shell.
Other env vars present on every leaked daemon: VSCODE_IPC_HOOK_CLI, VSCODE_WSL_EXT_LOCATION, VSCODE_RECONNECTION_GRACE_TIME, VSCODE_HANDLES_SIGPIPE, OTEL_*, COPILOT_OTEL_*. So the leaked process inherits the full Claude-VSCode-WSL shell environment.
Age distribution (from ps etime, in days since start):
24 today
2533 1 day
736 2 days
76 3 days
1116 4 days
1134 5 days (since this WSL VM booted)
That's continuous accumulation, not a one-time burst. ~1100 leaked daemons/day = ~46/hour, which matches typical Bash + Task tool call rates during active use.
Mechanism (best guess)
- Claude Code spawns a fresh shell for each Bash tool call and each Task subagent.
- Something loaded into that shell's process tree calls into
libdbusbeforeDBUS_SESSION_BUS_ADDRESSis set. (The OTEL exporter and/or the VSCode IPC bridge are plausible candidates — noteOTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCEandCOPILOT_OTEL_EXPORTER_TYPEon every leaked daemon.) libdbusinvokesdbus-launch --autolaunch, which double-forks a session bus. Signature on every leaked process:/usr/bin/dbus-daemon --syslog --fork --print-pid 5 --print-address 7 --session— exactly the autolaunch invocation.- When the tool-call shell exits, Claude Code does not propagate SIGTERM to the daemon. The daemon was already detached by
dbus-launch's double-fork, so it isn't a direct child anyway. WSL's/initadopts it. - Multiplied by ~46/hour = thousands per day.
The two dbus-daemon --system processes on the box are unaffected; only --session leaks. The legitimate WSLg at-spi bus (started with explicit --address=unix:path=/mnt/wslg/runtime-dir/at-spi/bus_0) is unaffected — it has a live parent.
Workaround
Setting export DBUS_SESSION_BUS_ADDRESS=disabled: in ~/.zshrc / ~/.bashrc stops libdbus from autolaunching. For a typical headless dev workflow on WSL2 this has no observable impact (no notify-send, no libsecret, no systemctl --user).
Cleanup of existing orphans (safe — kills only daemons reparented to /init with no children):
INIT_PID=$(pgrep -of /init) # WSL2 init pid
for pid in $(pgrep -f 'dbus-daemon.*--session'); do
ppid=$(awk '{print $4}' /proc/$pid/stat 2>/dev/null)
[ "$ppid" = "$INIT_PID" ] && [ -z "$(pgrep -P $pid)" ] && kill -TERM $pid
done
Suggested fix directions
- In the per-tool-call shell launcher, set
DBUS_SESSION_BUS_ADDRESS=disabled:(or a project-scoped bus) in the child env unless the user has explicitly set it. Costs nothing for users who don't use dbus, doesn't interfere with users who do. - Investigate whether the OTEL exporter / VSCode IPC native module is the actual
libdbusconsumer — if so, gate the dbus connection behind an availability check. - Track tool-call-spawned shells and
kill(SIGTERM, -pgid)the entire process group on completion, so any side-effect daemons die with the shell.
Related issues
- #26987 (VS Code extension leaks Claude processes when closing tabs/panels — WSL2): same environment, different trigger; this issue is the per-tool-call analog.
- #19045 (Task tool subagent processes not terminated after parent session ends — Linux): same lifecycle gap, different leaked process.
- #20369 (Orphaned subagent process leaks memory when parent terminal session terminated)
- #23154 (Subagents can't run TaskStop. Can orphan their own Bash tasks.) — overlaps with the Bash-tool half of this report.
- #33947 (MCP server and subagent processes not cleaned up on session end — orphan accumulation, PPID=1)
- #40667 (MCP server processes leak on host after subagent/session termination)
Why this matters
5620 idle dbus-daemon processes are ~3-5 MB RSS each = several GB of RAM held indefinitely, plus PID-table and FD-table pressure. On long-running WSL sessions this contributes meaningfully to the OOM patterns reported in #2938 and #18048.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗