Windows reserved device names break git (AUX, CON, PRN, NUL, COM, LPT)
TL;DR
Windows reserves a handful of file and folder names for DOS-era hardware devices. They cannot be used as a file or folder name anywhere in a path:
CON,PRN,AUX,NULCOM0–COM9,COM¹,COM²,COM³LPT0–LPT9,LPT¹,LPT²,LPT³
The rule is case-insensitive (aux = AUX = Aux) and applies even with an extension (aux.txt, nul.tar.gz are also treated as the device).
It applies to any segment of the path — public/aux/foo.svg fails for the same reason as aux/foo.svg.
Problem
In a Cangjie-practice project, 495 auxiliary glyph SVGs were downloaded into public/aux/, ready to git add and push.
Symptoms
$ git add public/aux/Cjem-a0-1.svg
error: open("public/aux/Cjem-a0-1.svg"): No such file or directory
error: unable to index file 'public/aux/Cjem-a0-1.svg'
fatal: adding files failed
Yet the same file:
ls public/aux/Cjem-a0-1.svglists it with the correct sizeGet-Content public/aux/Cjem-a0-1.svgreads its contentsstatreports all metadata as normalgit ls-files --others --exclude-standard public/aux/enumerates it
Copying the same file to public/_test_dir/test.svg makes git add succeed immediately. So the problem is not the file — it is the directory name aux.
Environment
- Windows 11
- Git for Windows (mingw-compiled
git.exe) - PowerShell 7+ and
cmd.exe - NTFS volume
The clue that gave it away
Trying Rename-Item public/aux public/aux_temp produced:
Cannot rename the specified target, because it represents a path or device name.
The phrase "represents a path or device name" is the giveaway — Windows is treating aux as a device, not a folder.
Solution
-
Rename via
cmd.exe(PowerShell'sRename-Itemrefuses, butcmd'smoveworks):cmd /c "move public\aux public\auxiliary" -
Update every reference in code:
<img src="aux/...">→<img src="auxiliary/...">,OUT_DIR = 'public/aux'→'public/auxiliary'. -
git addsucceeds immediately afterwards.
Root cause
A compatibility burden carried forward from MS-DOS. The OS reserved names for hardware devices (auxiliary device, console, printer, the null sink) so programs could "read and write devices as if they were files."
DOS-era example — sending text to the screen:
COPY my_text.txt CON
This "copies" the contents of my_text.txt to the CON (console) device.
To keep three-decade-old programs running, Windows preserves the rule. Even on Windows 11, Get-Content > CON in PowerShell still writes to the console.
Why ls / stat / Get-Content see the folder but git's open() does not
Two different syscall paths:
- Directory enumeration (
FindFirstFile/readdir) lists actual NTFS entries, including a directory literally namedaux. Sols,stat, andgit ls-filesall see it. - Opening by path (
CreateFile/ POSIXopen) makes Windows perform "DOS device name resolution" before touching disk. When it seesaux, it opens the device — never the directory of the same name on disk. So git'sopen("public/aux/...")receivesENOENT.
PowerShell's Get-Content goes through .NET, which in some versions automatically prefixes \\?\ to bypass the resolver — so it reads the file fine. But git.exe is a mingw-compiled native program calling Win32 APIs directly, without that workaround.
Trivia: \\?\ prefix bypasses the rule
Windows APIs accept a \\?\ path prefix that skips all DOS path parsing, including the device-name check.
# Normal mkdir is blocked
mkdir aux
# Cannot create the specified target...
# With the \\?\ prefix it succeeds
[System.IO.Directory]::CreateDirectory('\\?\D:\test\aux')
Do not actually do this. The resulting aux folder cannot be opened in File Explorer or removed with rm / Remove-Item — only further \\?\ calls can manipulate it. It is very easy to create something you cannot clean up.
If the word aux is semantically necessary, use a variant: aux_, auxi, auxiliary, aux-shapes.
Checklist: filter before creating a new file or folder
In any cross-platform project, before committing to a directory or file name, verify:
- Not one of
CON / PRN / AUX / NUL - Not one of
COM0–COM9 - Not one of
LPT0–LPT9 - Not used as the base name with an extension (
aux.txt,nul.jsonare also blocked) - If sharing code with teammates: a name that works fine on Linux/macOS may explode on Windows
Anyone who hits this typically assumes their Git install is broken, the file is corrupt, or it is a permissions issue — and only finds the real cause after a long detour.
References
- Microsoft Docs — Naming Files, Paths, and Namespaces
- Full reserved list:
CON,PRN,AUX,NUL,COM0–COM9,LPT0–LPT9 \\?\prefix details: Microsoft Docs, "Maximum Path Length Limitation" section