[BUG] Windows: stray 0-byte `NUL` file created in the working directory on every session start (still broken on 2.1.229 — prior fixes verified with Test-Path, which cannot see the file)

Status Open
Reported on v2.1.229
Maintainer reply None cached
Activity 0 comments · opened Aug 12, 2026

Summary

A stray 0-byte file named NUL is created in the working directory every time a Claude Code session starts on Windows. This is reported repeatedly and has been closed as fixed/duplicate many times (#30247, #23942, #4928, #17886, #20568, #23343, #17005, #15398, #25968, #16642, #31240 …), but it is not fixed as of 2.1.229.

Two things make this bug survive its own fixes:

  1. The file is invisible to normal Windows path APIs. Test-Path .\NUL returns False even while the file physically exists and Get-ChildItem lists it — Windows resolves the reserved name NUL to the null device instead of the on-disk entry. Any verification script that checks for it with a normal path call reports "clean." Detection/cleanup requires the \\?\ prefix (this was already noted in #31240, also closed).
  2. It appears to be first-run-only. Once NUL exists it is silently rewritten in place, so users only ever notice the moment it is created. It is actually created on every session.

Environment

| | |
|---|---|
| Claude Code | 2.1.229 (native binary, ~/.local/bin/claude, 307 MB) |
| OS | Windows 11 Pro 10.0.26200 |
| Shells tested | PowerShell 7.6.4 and Git Bash — both reproduce |

Reproduction

mkdir C:\temp\nulrepro
cd C:\temp\nulrepro
claude -p "hi" --model haiku
Get-ChildItem -Force          # -> NUL, 0 bytes
Test-Path -LiteralPath .\NUL  # -> False   <-- this is the trap

100% reproducible. Fresh empty directory, and also in a fresh git init directory.

Evidence that this is startup, not a tool call

In my repro the session never reached a model turn — it exited immediately with Credit balance is too low:

--- before ---
total 0
drwxr-xr-x 1 wolve 197609 0 Aug 12 14:45 .
drwxr-xr-x 1 wolve 197609 0 Aug 12 14:45 ..
⚠ claude.ai connectors are disabled because ANTHROPIC_API_KEY ...
Credit balance is too low
--- after ---
-rw-r--r-- 1 wolve 197609 0 Aug 12 14:45 NUL

Zero tool calls, zero model output, NUL still created. So this is not the Bash tool and not model-authored 2>NUL (which is the assumption behind several of the earlier closures, e.g. #76042). It happens during session startup.

Entry-point bracketing, each in its own clean directory:

| Command | NUL created |
|---|---|
| claude --version | no |
| claude mcp list | no |
| claude -p "hi" | YES |

Repeat runs in the same directory: created on run 1, 2 and 3 (file mtime updates each time — CreationTime 2:39:14 PM, LastWriteTime 2:43:03 PM on my real project directory).

Not a Git Bash / MSYS artifact

Earlier closures attributed this to MSYS mangling >NUL. That is not the cause here — it reproduces identically when launched from PowerShell 7.6.4 with no Git Bash in the process tree.

Not the bundled string either

Grepping the shipped 307 MB binary finds no occurrence of 2>NUL, > NUL, >NUL, 2>nul, or the quoted string "NUL". The 118 hits for >nul are all .catch(()=>null) false positives. The generated shell snapshot (~/.claude/shell-snapshots/snapshot-bash-*.sh) also contains no NUL redirect. So the path is most likely being constructed at runtime — e.g. a process.platform === 'win32' ? 'NUL' : '/dev/null' device path that is then passed through path.resolve()/path.join() before being opened. path.resolve(cwd, 'NUL') yields C:\<cwd>\NUL, which is a real file rather than the null device, which would explain the exact observed behavior.

Comparison

Running codex exec in an identical fresh directory on the same machine leaves the directory completely empty. This is Claude Code specific.

Impact

  • Pollutes every project directory a user ever opens; shows up in git status as an untracked file.
  • Reserved-name files are hard to remove with normal tooling — del NUL, Remove-Item NUL and most GUI tools fail because the name resolves to the device; users need del "\\?\C:\path\NUL".
  • Breaks OneDrive / SharePoint / Dropbox sync (the original motivation for #16604 and #15799).
  • Breaks tarballs, archive extraction and file copies onto non-Windows systems and CI.

Ask

  1. Reopen / re-triage rather than closing as duplicate — the existing closed issues have no working fix on 2.1.229.
  2. When verifying a fix, check for the file with Get-ChildItem -Force or a \\?\-prefixed path. A Test-Path .\NUL check will report success even when the bug is present.
  3. Null-device paths should be passed to the OS verbatim, never through path.resolve()/path.join(). On Node/Windows the correct value is os.devNull (\\.\nul).

View original on GitHub ↗