[Bug] Bash tool children on Windows can't unlink AF_UNIX sockets (error 1920, path poisoned) — breaks unix-socket daemons

Open 💬 2 comments Opened Jul 10, 2026 by DanielDantasL

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?

On Windows, any process launched from inside a Claude Code session's process tree (the Bash
tool and its descendants) exhibits a broken AF_UNIX socket lifecycle:

  1. The socket file is not auto-removed when the socket closes (on healthy Windows it is).
  2. Deleting it fails with error 1920 (ERROR_CANT_ACCESS_FILE, "The file cannot be

accessed by the system").

  1. The name becomes poisoned: Test-Path/stat say the file doesn't exist, yet creating a

directory over the same path fails with "already exists". The path is unusable from then on,
and the poisoning outlives the session, the Claude Code version, and reboots.

The same operations from a plain user terminal on the same machine, same user, same binaries,
work perfectly
(socket file auto-removed at close). The defect follows the Claude Code process
tree, not the machine.

Two further manifestations observed in the same contexts:

  • Named-pipe access degradation: processes spawned from the Claude Code tree could no

longer even open a healthy, user-started daemon's named pipe, while a plain terminal
opened it fine. The client then tried to auto-start a second daemon, which self-poisoned.

  • Unkillable children: one poisoned daemon spawned from the Claude Code tree became a

zombie holding its state DB open; a normal kill failed and it required an elevated
taskkill (one instance resisted even that until reboot).

Practical consequence: any software that binds AF_UNIX sockets at fixed paths is permanently
broken when started from Claude Code. Docker Sandboxes' sandboxd daemon is the concrete
victim: started via a Claude-spawned sbx command it self-poisons its socket paths on first
start and can never start again, requiring state surgery. We lost several days to this
before isolating the cause, because it presents as the victim software's bug.

What Should Happen?

Child processes of the Bash tool should have normal Windows AF_UNIX socket semantics —
identical to running the same command in a plain terminal:

  • On socket close, Windows auto-removes the socket file (closed; file exists: False).
  • If a socket file does linger, DeleteFile/Remove-Item on it succeeds.
  • After deletion, the path name is immediately reusable.

No handle to a child's socket should remain alive anywhere in the harness process tree after
the child has closed it and exited.

Error Messages/Logs

Probe output from inside a Claude Code session (2.1.206, Bash tool, 2026-07-10; identical
output captured on 2.1.198 earlier the same day, freshly restarted session in each case):

probe path: C:\Users\<user>\AppData\Local\Temp\afunix-probe.sock
NOTE: leftover probe file from a previous run is still undeletable; using a unique name.
probe path: C:\Users\<user>\AppData\Local\Temp\afunix-probe-3cd47e20.sock
bound+listening; file exists: True
closed; file exists: True
UNLINK FAILED: The file cannot be accessed by the system. : 'C:\Users\<user>\AppData\Local\Temp\afunix-probe-3cd47e20.sock'.
MKDIR OVER NAME FAILED: An item with the specified name C:\Users\<user>\AppData\Local\Temp\afunix-probe-3cd47e20.sock already exists.

RESULT: BROKEN - AF_UNIX socket files become undeletable ghosts on this machine.

Note the first line: the ghost left by probe runs from previous days and previous Claude
Code versions
is still undeletable.

The same script run moments apart from a plain terminal on the same machine:

bound+listening; file exists: True
closed; file exists: False
socket file auto-removed on close (healthy Windows behavior)
RESULT: healthy - AF_UNIX socket cleanup works normally.

Downstream victim symptom (Docker Sandboxes daemon, once poisoned): start/stop loop in its
daemon.log, database already in use from second-daemon attempts, ghost .sock files in its
state directory that cannot be deleted.

Steps to Reproduce

  1. Save the following as afunix_probe.ps1 (requires PowerShell 7+ for

UnixDomainSocketEndPoint):

``powershell
$p = Join-Path ([System.IO.Path]::GetTempPath()) 'afunix-probe.sock'
if (Test-Path $p) { Remove-Item $p -Force -ErrorAction SilentlyContinue }
$ep = [System.Net.Sockets.UnixDomainSocketEndPoint]::new($p)
$s = [System.Net.Sockets.Socket]::new('Unix', 'Stream', 'Unspecified')
$s.Bind($ep); $s.Listen(1)
"bound; file exists: $(Test-Path $p)"
$s.Close(); $s.Dispose()
"closed; file exists: $(Test-Path $p)" # healthy Windows: False (auto-removed)
if (Test-Path $p) {
try { Remove-Item $p -Force -ErrorAction Stop; "unlink OK" }
catch { "UNLINK FAILED: $($_.Exception.Message)" } # broken: error 1920
try { New-Item -ItemType Directory -Path $p -ErrorAction Stop | Out-Null; "name clean" }
catch { "NAME POISONED: $($_.Exception.Message)" } # broken: 'already exists'
}
``

  1. Run it from a plain user terminal:

pwsh -NoProfile -File afunix_probe.ps1
closed; file exists: False (auto-removed, healthy).

  1. Start a Claude Code session on the same machine and ask Claude to run the same command

via its Bash tool:
pwsh -NoProfile -File afunix_probe.ps1
→ file still exists after close, UNLINK FAILED ... The file cannot be accessed by the
system
(error 1920), then the mkdir-over-name check fails.

  1. Observe that each run from step 3 leaves one undeletable ghost file in %TEMP%, which

remains undeletable in later sessions and after reboot.

The context comparison is the point of the repro: identical script, identical user, identical
machine — only the ancestry of the process differs.

Claude Model

Fable 5

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.206 (Claude Code)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

Environment

| | |
|---|---|
| OS | Windows 11 Pro 25H2, build 10.0.26200.8737 |
| Claude Code | 2.1.206 (latest at time of writing) — reproduced live 2026-07-10; also reproduced identically on 2.1.198 the same day (native installer) |
| Bash tool shell | Git Bash (MSYS) |
| Probe runtime | PowerShell 7 (.NET UnixDomainSocketEndPoint); also reproduced by a Go binary (Docker's sandboxd) |
| EDR | SentinelOne (ruled out — below) |

When it was first observed — honest scoping: first observed 2026-07-06. We initially
read this as a regression (sbx worked fine on 2026-07-02), but on re-examination the 07-02
sessions most likely had the daemon started from a user terminal, so the AF_UNIX cleanup
path was probably never exercised from the Claude Code process tree before 07-06. We
therefore cannot distinguish between (a) a regression in early July 2026 and (b) a
long-standing bug that a Claude-spawned daemon start first exposed on 07-06. What we did
verify: no machine-level change explains the 07-06 breakage (see below), and the bug
reproduces on 2.1.198 and 2.1.206.

What we ruled out (so you don't have to ask):

  • SentinelOne EDR — exonerated. Reproduced with Device Control disabled + reboot, all four

S1 kernel drivers in Stopped state, and no S1 DLL injected into the probing process
(verified via loaded-module list). Also reproduced identically before and after an S1
upgrade (22.3 → 25.2.6.442).

  • Windows June cumulative update — the same OS build (and afd.sys) that shows the broken

behavior from the Claude Code tree shows fully healthy AF_UNIX cleanup from a plain terminal,
today. An OS-level defect would not select on process ancestry.

  • Google Drive File Stream, filesystem minifilters — all loaded minifilters were

Microsoft's; Drive FS driver predates the onset by a year.

  • "The machine is broken" — no: the probe from a plain terminal is fully healthy. Only the

Claude Code process tree exhibits the defect.

Hypothesis: on Windows, an AF_UNIX socket file can only be unlinked once no handles to the
underlying socket remain, and healthy closure removes the file automatically. The observed
behavior — file lingers after Close()+Dispose() in the child, unlink returns 1920 — is
exactly what handle duplication/inheritance into a longer-lived ancestor process would
produce: some process in the Claude Code harness tree is holding a duplicated handle to
sockets its descendants create, keeping the kernel object alive after the child has closed it.
The likely home for that is the harness's Bash-tool spawn path (handle inheritance flags, job
objects, or the shell-snapshot mechanism) — #68858 below independently shows stray handle
inheritance in that path. Whether this arrived with an early-July update or has been present
longer is unknown (see the scoping note above). One wrinkle we can't fully explain under a pure
live-handle theory: ghost files remain undeletable across reboots (we expect chkdsk
would be needed to reclaim them). We note it as-is.

Possibly related open issues (checked 2026-07-10 — no existing issue covers this exact
bug):

  • #68858 "Windows: uv-based (uvx)

MCP servers fail to start — child uv inherits a handle into its own build temp dir" —
independent confirmation of stray handle inheritance in the Windows spawn path, the same
mechanism hypothesized above; ours is the mirror image (a lingering duplicated handle keeps
the child's socket alive instead of blocking its dir).

  • #76353 "Bash tool leaks orphaned

child processes on Windows timeout" — leaked/held child handles would produce exactly the
socket-lifetime symptoms here (our unkillable-child manifestation is also an orphaned child).

  • #73543 "Local stdio MCP server

fails with 'access denied for socket file' ... on Windows" — different manifestation
(spawn-time access denial vs cleanup failure), opened 2026-07-02, a few days before we first
observed ours.

Workarounds in use: all daemon lifecycle operations run from the user's own terminal,
never via Claude's Bash; Claude-spawned sbx commands limited to ones that cannot auto-start
the daemon; ghost files left in %TEMP% and the sbx state dir pending chkdsk.

Artifacts available on request: the full probe script with verdict logic
(afunix_probe.ps1); timestamps of good/bad sessions bracketing the onset window;
process/module inventories from the EDR-exoneration runs; sandboxd daemon logs showing the
poisoned-restart loop.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗