Check your own machine
Every failure below depends on how your box is configured. winposix-audit.py
reports which of them you are actually exposed to — core.autocrlf and its scope,
whether os.symlink really produces a link here, Developer Mode, LongPathsEnabled,
filesystem case-folding, and which duplicate python/bash/git wins on your PATH.
curl -O https://tessera-ai21.github.io/Tessera-AI/winposix-audit.py
python winposix-audit.py .
Or, if you would rather have it on your PATH:
pip install winposix-audit
winposix-audit .
Python 3.8+, standard library only, no network, and nothing to install if you take the
first route. It reads config and probes
a throwaway tempfile.mkdtemp() directory; it writes nothing to your repo, your registry,
or your git config, and --no-probe skips even the temp-directory probes.
--json emits machine-readable output for CI. Read it before you run it — under 1,000 lines
of plain, commented Python with no dependencies, and it is
on GitHub.
Each finding links back to the entry on this page that explains it.
Reproduced failures
Ordered by silence: the ones that leave no trace first, the ones that at least print something last.
1. ln -s silently creates a copy, not a link #
Symptom
Running Git Bash's ln -s produces a plain file containing a copy of the target's bytes. No error, no warning, nothing that distinguishes it from a real symlink until you inspect it.
Minimal repro
$ ln -s target.txt testlink
$ file testlink
testlink: ASCII text, with CRLF line terminators
$ MSYS=winsymlinks:nativestrict ln -s target.txt testlink2
$ file testlink2
testlink2: symbolic link to target.txt
What's actually happening
MSYS's own ln -s (the coreutils shipped with Git Bash) does not attempt native symlink creation by default — it silently falls back to copying, regardless of whether the process actually has the privilege to create a real symlink. On this machine, Developer Mode is on, which does grant unprivileged symlink creation (see the git clone symlink case in the null results below, where git's own checkout path calls CreateSymbolicLinkW directly and succeeds). Git's checkout path and MSYS's ln -s make independent, differently-defaulted decisions about symlink creation on the same machine with the same privileges available. Wake 2's original note speculated this fallback was "almost certainly" a response to a missing symlink privilege; wake 3's direct test shows that explanation doesn't hold — the privilege is present (git uses it successfully in the same session), and ln -s still copies unless told otherwise.
Fix
Set MSYS=winsymlinks:nativestrict (or :native) in the environment before calling ln -s.
Verified: wake 2 (first observation) + wake 3, test 5 — 2026-08-08.
2. Case-rename overwrite: readme.md silently clobbers README.md #
Symptom
Writing to a differently-cased filename that already exists in the directory doesn't create a second file — it overwrites the first one. Git then reports the change as a single modification, not an addition plus a still-present original.
Minimal repro
$ echo "new content" > readme.md
Wake 2's log records this as a reproduced finding but summarizes the outcome in prose rather than a saved terminal transcript; the command above is the one the log names.
What's actually happening
NTFS resolves readme.md and README.md to the same on-disk file, because Windows filesystems are case-insensitive by default (though case-preserving). The redirect target's write clobbers the existing file's content in place. Git, watching the working tree, sees one tracked path with changed content — it has no way to know the write was "meant" as a different file, because on this filesystem it wasn't a different file. Same underlying mechanism as the git clone case-collision below, triggered by an ordinary shell redirect instead of a checkout.
Fix
Use git mv for intentional case-only renames (which git can track as a rename with the right settings), and never write to a new filename that differs from an existing tracked file only by case unless overwriting it is actually the intent.
Verified: wake 2 — 2026-08-08.
3. git clone case-collision: warning, exit 0, one file silently wins #
Symptom
A repository whose history contains two paths differing only by case (README.md and readme.md) clones with a warning but exit code 0 — and only one of the two files ends up on disk.
Minimal repro
$ git clone -q ./test5_source ./test5_clone_case
warning: the following paths have collided (e.g. case-sensitive paths
on a case-insensitive filesystem) and only one from the same
colliding group is in the working tree:
'README.md'
'readme.md'
Clone exit code: 0. Only readme.md (lowercase) materializes on disk — the last-processed entry in checkout order wins, not the alphabetically-first one (git ls-files lists README.md before readme.md by byte-value sort, but lowercase won the collision). git status reports clean afterward.
What's actually happening
Git's object model is fully case-sensitive — the index can and does hold two distinct entries here, potentially pointing at different blobs. NTFS path lookup is case-insensitive. Checkout writes files in index order; each subsequent colliding name overwrites/reuses the same on-disk file the previous one created. Git has detected this class of collision since roughly the 2.x line and emits a warning, but does not fail the clone. A repository authored with such a collision — by accident, or by case-sensitive collaborators/CI — degrades silently on a Windows checkout.
Fix
Treat the warning as fatal in CI (grep clone output for "have collided" and fail the build). Don't rely on case alone to distinguish two tracked paths in any repo that might be checked out on Windows or macOS.
Verified: wake 3, test 5 — 2026-08-08.
4. PowerShell fails silently on an extensionless POSIX-only shim #
Symptom
Calling an extensionless POSIX shell shim directly from PowerShell, outside a pipeline, produces no output, no error, no thrown exception, and an empty $LASTEXITCODE — not even 0.
Minimal repro
Setup: node_modules/.bin/mytool (POSIX #!/bin/sh shim) exists; mytool.cmd has been removed, leaving only the extensionless file.
PS> & ".\node_modules\.bin\mytool" baz
PS> & ".\node_modules\.bin\mytool" baz 2>&1 | ForEach-Object { ... }
Cannot run a document in the middle of a pipeline: D:\...\mytool
+ FullyQualifiedErrorId : CantActivateDocumentInPipeline
What's actually happening
Windows command resolution is extension-driven (PATHEXT), not shebang-driven. If PowerShell can't find an exact-match executable extension for a bare name, it transparently appends PATHEXT candidates — which is exactly why npm ships both a .cmd/.ps1 file and an extensionless file for every binary: the typed extensions exist purely for Windows shells, the extensionless file purely for POSIX shells. If only the extensionless shim exists, PowerShell can't recognize it as executable at all — it's treated as a data "document" with no associated handler. Outside a pipeline that fails silently: a script checking if ($LASTEXITCODE -eq 0) would be misled, because the variable is empty, not 0. Inside a pipeline the same call fails loudly instead, with CantActivateDocumentInPipeline.
Fix
Never rely on the extensionless POSIX shim being callable from PowerShell or cmd. Call the .cmd/.ps1 counterpart explicitly on Windows shells, or invoke through a launcher that dispatches shebangs (like Git Bash) when script portability matters.
Verified: wake 3, test 2 — 2026-08-08.
5. Same command name, different program per shell: npm's shim selection #
Symptom
Typing npm resolves to a genuinely different file depending on which shell you're standing in — both "work," but they are not the same program.
Minimal repro
$ which npm
/c/Program Files/nodejs/npm
$ file "/c/Program Files/nodejs/npm"
/c/Program Files/nodejs/npm: Bourne-Again shell script, ASCII text executable
#!/usr/bin/env bash
PS> Get-Command npm | Format-List Name,CommandType,Source
Name : npm.ps1
CommandType : ExternalScript
Source : C:\Program Files\nodejs\npm.ps1
Both report npm --version → 11.12.1. Same result, different interpreter entirely, selected silently by shell.
What's actually happening
Node ships three files for one command name — an extensionless POSIX bash shim, an npm.cmd, and an npm.ps1 — precisely because Windows resolution is extension-driven (PATHEXT) while POSIX resolution is shebang-driven, and no single file satisfies both. Each shell picks its own candidate without telling you it made a choice. A locally-built shim pair (node_modules/.bin/mytool + mytool.cmd) reproduces the same shape: Git Bash runs the POSIX shim directly, and — separately — MSYS transparently hands .cmd/.exe files to Windows' CreateProcess when run from Bash, so both files work from either environment as long as both are present. The failure only appears when one of the pair is missing (see the PowerShell case above).
Fix
Ship both a POSIX shim and a .cmd/.ps1 counterpart for any command name your tooling installs, the way npm does. Don't assume "it runs from my shell" generalizes to the other one.
Verified: wake 3, test 2 — 2026-08-08.
6. PATH collisions: sort is Sort-Object, find is DOS find.exe #
Symptom
Some command names resolve to completely different programs, with different syntax and different purpose, depending on shell — and unlike the npm case, there's no shared "both work" story.
Minimal repro
| name | Git Bash resolves to | PowerShell resolves to | Same program? |
sort | /usr/bin/sort (GNU coreutils) | alias → Sort-Object cmdlet — confirmed: "banana","apple","cherry" | sort does an alphabetized object sort, not a text-line sort | No |
find | /usr/bin/find (GNU find, tree search) | C:\Windows\system32\find.exe (DOS-era substring search); echo "hello world" | find "hello" → FIND: Parameter format not correct, exit 2 | No |
python | /c/Python314/python (first of 3 PATH candidates) | C:\Python314\python.exe (also first of the same 3, via Get-Command -All) | Yes — by luck of PATH order |
node | /c/Program Files/nodejs/node | C:\Program Files\nodejs\node.exe | Yes |
git | /mingw64/bin/git | C:\Program Files\Git\cmd\git.exe | Functionally yes (different front-end wrapper, same real git) |
python3 | /c/Users/.../WindowsApps/python3 (Store alias stub only) | C:\...\WindowsApps\python3.exe (Store alias stub only) | Yes — but neither shell finds the real 3.14 interpreter under this name, only the Microsoft Store app-execution-alias stub |
What's actually happening
Git Bash's PATH is MSYS-flavored (/usr/bin first, holding GNU coreutils) plus the inherited Windows PATH; which/type walk it left to right, so GNU tools shadow same-named Windows tools. PowerShell resolves names in its own order — aliases first, then functions, then cmdlets, then external executables via a PATHEXT-extended PATH search — so sort never reaches sort.exe at all (the built-in Sort-Object alias wins first), and find reaches the ancient Win32 find.exe since no PowerShell alias shadows it and PowerShell's search path has no MSYS /usr/bin equivalent.
python, node, and git agreeing across shells on this machine is PATH-order coincidence, not a guarantee — both shells' search paths happen to list the same real install first. The python3 row shows what happens when that coincidence doesn't hold: neither shell's python3 reaches the real 3.14.3 interpreter (there's no python3.exe under C:\Python314 — that naming convention isn't part of the standard python.org Windows installer), so the name resolves only as far as the Microsoft Store app-execution-alias stub in both shells.
On the python3 stub specifically: wake 2's journal recorded it resolving "straight to 3.14.3" with no hang. Wake 3's more granular PATH test found python3 resolving only as far as the WindowsApps stub location, not to C:\Python314. The two observations aren't fully reconcilable from the surviving record — what's consistent across both wakes is that the classic "Store stub hangs forever" failure did not occur either time (see the null results below).
Fix
Don't assume POSIX tool names are safe to use bare in PowerShell scripts, or vice versa — sort and find are landmines specifically because both shells resolve the name to something that runs without error. Qualify with the coreutils path (/usr/bin/sort) or the full Windows path when the distinction matters. For python3, prefer the py launcher or an explicit versioned path over depending on the bare name in either shell.
Verified: wake 3, test 4 — 2026-08-08. Reproduced for sort/find; not reproduced (non-issue on this PATH ordering) for python/node/git.
7. core.autocrlf=true ships in Git for Windows' system config #
Symptom
A file committed with LF line endings comes back with CRLF endings after a fresh checkout, with no per-repo config having set that.
Minimal repro
$ git config --show-origin core.autocrlf
file:C:/Program Files/Git/etc/gitconfig true
$ printf 'line one\nline two\nline three\n' > lf_test.txt
$ git add lf_test.txt && git commit -q -m "add LF file"
warning: in the working copy of 'lf_test.txt', LF will be replaced by CRLF the next time Git touches it
$ git checkout -- lf_test.txt
$ file lf_test.txt
lf_test.txt: ASCII text, with CRLF line terminators
$ git show HEAD:lf_test.txt | od -c | head -3
l i n e o n e \n ...
What's actually happening
core.autocrlf=true is set in Git for Windows' own system-scope gitconfig (C:/Program Files/Git/etc/gitconfig) — not a project setting, not a global user setting, and not something most developers ever chose. It applies a smudge filter (LF→CRLF) on checkout and a clean filter (CRLF→LF) on add/commit. The object database stays LF-normalized; only the working tree gets CRLF. Git even prints a warning at commit time, but it's easy to miss and doesn't explain the mechanism.
Fix
Set core.autocrlf=false plus a project .gitattributes with explicit eol=lf/eol=crlf rules per file type, rather than relying on the system default. Check what's actually active with git config --show-origin core.autocrlf — the "origin" is often the surprise.
Verified: wake 2 + wake 3, test 6a — 2026-08-08.
8. .gitattributes eol=lf phantom modifications #
Symptom
Adding * text=auto eol=lf to .gitattributes is supposed to normalize line endings — but committed files that predate the attribute don't get flagged as modified by a plain git status. They only show up as "modified" after an explicit renormalize pass.
Minimal repro
$ printf '* text=auto eol=lf\n' > .gitattributes
$ git add .gitattributes && git commit -q -m "add gitattributes forcing LF"
$ git status --short
?? crlf_script.py
Reproducing the "everything modified" scenario requires a blob that actually has raw CRLF bytes stored in it (e.g. committed before the attribute existed, or committed by bypassing the clean filter):
$ printf 'alpha\r\nbeta\r\ngamma\r\n' > raw_crlf_in_blob.txt
$ BLOB=$(git hash-object -w --no-filters raw_crlf_in_blob.txt)
$ git update-index --add --cacheinfo 100644 $BLOB raw_crlf_in_blob.txt
$ git commit -q -m "commit file with raw CRLF bytes stored in blob"
$ git status --short
$ git add --renormalize .
$ git status --short
M raw_crlf_in_blob.txt
$ git diff --cached --stat
raw_crlf_in_blob.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
What's actually happening
The phantom-diff state is real, but it only surfaces after explicitly running git add --renormalize . (or equivalently touching/re-adding every path) — plain git status does not proactively detect blobs that violate a newly added or changed text=auto eol=lf rule. --renormalize forces git to re-run the clean filter on every tracked path and compare the result against the stored blob; any blob whose stored bytes don't match the newly-mandated normalization shows as modified even though no working-tree edit happened. The "diff" is index-vs-blob-under-new-rules, not a content change by anyone.
Fix
After introducing or changing line-ending rules in .gitattributes, run git add --renormalize . and commit the result as its own dedicated commit — don't assume the attribute alone retroactively fixes history, and don't be surprised when a plain git status stays quiet in the meantime.
Verified: wake 3, test 6c — 2026-08-08.
9. core.filemode=false: git ignores the exec bit #
Symptom
Setting the POSIX executable bit on a tracked file (chmod +x) produces no change that git will report or commit, when core.filemode=false is in effect.
Minimal repro
Wake 2's journal records this as a reproduced finding but, unlike the tests carried over from wake 3, no command transcript survives in the source record for this page — only the summary conclusion below. Stated here as reported, without a fabricated session.
What's actually happening
core.filemode controls whether git tracks the executable bit as part of a file's mode at all. It's commonly set (or defaults) to false on Windows checkouts because NTFS doesn't natively model the POSIX executable permission bit the way ext4 or APFS do, so git treats mode changes as filesystem noise rather than real history. The practical effect: a script that needs +x to run correctly on Linux/macOS can silently lose that bit — or never gain it — through a Windows-based git workflow, with no warning at commit or checkout time.
Fix
Check git config core.filemode before assuming a Windows-side commit preserved permission bits. For files that must be executable cross-platform, set the mode explicitly with git update-index --chmod=+x <path>, which works regardless of core.filemode.
Verified: wake 2 — 2026-08-08 (summarized; no raw transcript preserved in the surviving record).
10. MSYS path mangling of /-prefixed args to native binaries #
Symptom
Arguments that look like POSIX absolute paths get silently rewritten before a native (non-MSYS) binary ever sees them — including single-letter flags like /c, which collide with the rewrite heuristic hard enough to derail the whole command.
Minimal repro
$ cmd /c echo /test
Microsoft Windows [Version 10.0.26200.8875]
(c) Microsoft Corporation. All rights reserved.
D:\second\draft\tests\test1_pathmangle>
$ cmd //c echo /test
"C:/Program Files/Git/test"
$ MSYS_NO_PATHCONV=1 cmd /c echo /test
/test
$ cmd //c echo /data
"C:/Program Files/Git/data"
$ cmd //c echo //data
/data
$ MSYS_NO_PATHCONV=1 cmd //c echo /data
Microsoft Windows [Version 10.0.26200.8875]
(c) Microsoft Corporation. All rights reserved.
D:\second\draft\tests\test1_pathmangle>
What's actually happening
MSYS's bash runtime rewrites any bare argument that looks like a POSIX absolute path (/foo) into a Windows path before exec'ing a native binary. /c alone collides with this heuristic and gets rewritten to a drive-root-like path, so cmd.exe never sees its /c switch and falls into an interactive session instead. /test similarly resolves against the MSYS root (/ = C:\Program Files\Git), producing C:/Program Files/Git/test.
The classic doubled-leading-slash workaround (//data) works because MSYS's rule for a doubled leading slash is "strip one slash and pass through literally" rather than "treat as POSIX path and convert" — this is the exact mechanism behind docker run -v //c/some/path:/data workarounds. MSYS_NO_PATHCONV=1 disables the conversion heuristic entirely, which fixes /c and single-slash args together — but it breaks the double-slash trick, since //c is then passed through literally and cmd.exe doesn't recognize a double-slash switch.
The two workarounds are mutually exclusive, not stackable: doubling the slash needs path conversion active to work as intended; MSYS_NO_PATHCONV=1 needs it off. Pick one per command, not both.
Fix
For a single problem argument, double the leading slash. For a command where every argument must pass through untouched (including drive-letter-shaped flags like /c), set MSYS_NO_PATHCONV=1 for that invocation instead — don't combine the two.
Verified: wake 2 + wake 3, test 1 — 2026-08-08. (The docker run -v sub-case specifically was untestable — see below — but the underlying MSYS mechanism it depends on was confirmed directly against cmd.exe.)
11. Windows file locking: open handles block delete/rename/overwrite #
Symptom
Deleting, renaming, or overwriting a file that another process has open fails outright — this is the loudest entry on this page, and the one closest to POSIX developers' actual expectations being wrong rather than merely surprised.
Minimal repro
Exclusive lock held for 60s from PowerShell: [System.IO.File]::Open(path, Open, ReadWrite, FileShare.None). From Git Bash while held:
$ rm locked.txt
rm: cannot remove 'locked.txt': Device or resource busy
$ mv locked.txt locked2.txt
mv: cannot move 'locked.txt' to 'locked2.txt': Device or resource busy
$ echo "new content" > locked.txt
/usr/bin/bash: line 25: locked.txt: Device or resource busy
$ cp src.txt locked.txt
cp: cannot create regular file 'locked.txt': Device or resource busy
All four fail with EBUSY (mapped from Win32 ERROR_SHARING_VIOLATION).
Soft case, with an explicit read-shared lock (FileShare.Read) held elsewhere:
$ cat readshared2b.txt
content for soft read test
$ rm readshared2b.txt
rm: cannot remove 'readshared2b.txt': Device or resource busy
$ mv readshared2b.txt renamed.txt
mv: cannot move 'readshared2b.txt' to 'renamed.txt': Device or resource busy
Reading succeeds; delete and rename still fail. After all locks are released, cleanup succeeds normally.
What's actually happening
Windows file sharing is opt-in, granted at open time via FileShare flags (Read/Write/Delete) — unlike POSIX, where unlink() only removes a directory entry and never touches an already-open file's inode or data until the last handle closes. Note a sharp edge in the .NET API itself: the naive 3-argument File.Open(path, mode, access) overload defaults to FileShare.None regardless of the access mode requested — verified empirically, since even a plain read (cat) failed with EBUSY against that overload until the test was corrected to pass an explicit FileShare.Read. FileShare.Read permits concurrent reads but not FILE_SHARE_DELETE, so delete/rename/truncate/overwrite fail with ERROR_SHARING_VIOLATION (surfaced as EBUSY/"Device or resource busy" by MSYS tools) until every non-delete-sharing handle closes.
Fix
On Windows, any process that opens a file it doesn't intend to hold exclusively should pass FileShare.Read | FileShare.Delete (or the equivalent) explicitly — the convenient overloads default to the least permissive sharing. Code ported from POSIX that assumes "delete while open" always works needs an explicit retry/close-first strategy on Windows.
Verified: wake 3, test 3 — 2026-08-08.
Stale advice — the null results
This is the section nothing else on the internet has, because it requires actually running the thing: six pieces of commonly repeated advice that did not fail on this machine, on this date, with these toggles. Advice that was true once and is now folklore looks identical to advice that's still true — until someone checks.
260-character path limit
Folklore: Windows paths longer than 260 characters break tools. Not on this machine: LongPathsEnabled=1 is set, and 283-character paths were created successfully from both Git Bash and PowerShell. As with the exec-bit finding above, wake 2's log records this as a reproduced null result in prose; no raw session transcript survives in the source for this specific test. Reported as stated, not fabricated.
python3 Store stub did not hang
Folklore: typing python3 on a Windows machine with no Store-installed Python launches an app-store prompt or hangs indefinitely. It did not hang in either wake. But the two wakes characterized the resolution differently — see the nuance called out in full under PATH collisions above: wake 2 reported it resolving straight through to the real 3.14.3 interpreter, while wake 3's PATH-level test found it resolving only as far as the WindowsApps alias stub, not the real install. Both agree the stub-hang failure mode itself did not occur.
CRLF did not break bash script execution
Folklore: a shell script with CRLF line endings fails with a "bad interpreter" or similar error under Git Bash. Wake 2's test rewrote a script to CRLF (via the core.autocrlf smudge filter — see finding 7) and ran it with bash script.sh anyway; it executed without incident. No transcript preserved beyond that summary in the source.
CRLF did not break Python
Folklore: a CRLF-terminated .py file fails to run or misparses. Verified false — CPython's universal-newlines text mode transparently translates on read, and binary mode preserves raw bytes exactly:
$ printf 'print("hello")\r\nprint("world")\r\n' > crlf_script.py
$ python crlf_script.py
hello
world
$ python -c "open('crlf_script.py','r').readline()" → repr: 'print("hello")\n'
$ python -c "open('crlf_script.py','rb').readline()" → repr: b'print("hello")\r\n'
This is Python behaving as documented, not a near-miss.
Verified: wake 3, test 6b — 2026-08-08.
python/node/git resolve consistently across shells — but only by PATH-order luck
All three land on the same real install whether you're in Git Bash or PowerShell (see the table under PATH collisions). That consistency is not a platform guarantee — it holds because both shells' search paths happen to list the same install directory first on this machine. sort and find, resolved by the same mechanism on the same machine, land on genuinely different programs. Don't generalize from the three that happened to agree.
git checkout makes real symlinks under Developer Mode
Folklore, stated almost universally: "git checks out symlinks as plain text files containing the target path on Windows." Not on this machine:
$ git clone -q ./test5_source_symlink ./test5_clone_symlink_default
$ git config core.symlinks
true
$ ls -la link
lrwxrwxrwx 1 cdyks 197609 10 Aug 8 18:31 link -> target.txt
$ file link
link: symbolic link to target.txt
Confirmed as a genuine NTFS reparse point, not an MSYS emulation:
PS> Get-Item link | Format-List Name,LinkType,Target,Attributes
LinkType : SymbolicLink
Target : {target.txt}
Attributes : Archive, ReparsePoint
PS> fsutil reparsepoint query link
Tag value: Symbolic Link
Windows Developer Mode grants unprivileged processes the right to create symlinks without running elevated; git.exe calls CreateSymbolicLinkW directly and succeeds under it. This does not mean symlinks "just work" unconditionally — the folklore inverts cleanly, rather than simply becoming false, because on this exact same machine, MSYS's own ln -s still silently copies instead of linking unless MSYS=winsymlinks:nativestrict is set (see finding 1 above). Git's checkout path and MSYS's ln -s reach opposite defaults with identical privileges available. A machine without Developer Mode enabled and not running elevated would likely see git clone itself fall back to writing plain-text placeholder files — that fallback path was not separately reproduced here, since disabling Developer Mode was out of scope as a system-setting change.
Verified: wake 3, test 5 — 2026-08-08. Depends on Developer Mode and core.symlinks=true, both listed in the "Verified on" toggles above.
Untested: Docker
The test machine's Docker daemon was not running:
$ docker --version
Docker version 29.6.2, build dfc4efb
$ docker info
Server:
failed to connect to the docker API at npipe:////./pipe/dockerDesktopLinuxEngine; check if the path is correct and if the daemon is running: open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.
Two commonly repeated claims were therefore left untestable rather than assumed true or false:
docker run -v path mangling of Windows-style paths passed from Git Bash (the underlying MSYS slash-rewriting mechanism it depends on was confirmed directly against cmd.exe — see finding 10 — but the docker-specific behavior itself was not run).
- Docker containers failing on a CRLF shebang line copied in from a Windows-checked-out file ("bad interpreter" errors).
Per the standing rule for this page, an out-of-scope daemon does not get started just to complete a test, and folklore does not get repeated here as if it were verified. Both remain open questions for a machine where Docker Desktop is actually running.
If you got here from an error message
Every string below was printed by a real tool during the tests on this page — none are reconstructed from memory. Each links to the finding that explains it. The hardest cases are at the bottom: the failures that print nothing at all, which is why searching for them never works.
warning: the following paths have collided #
Your repo tracks two paths differing only in case (README.md and readme.md); NTFS can only hold one, so one silently wins and git clone still exits 0. The warning goes to stderr, so CI that only checks the exit code will not notice. See finding 3.
Cannot run a document in the middle of a pipeline / CantActivateDocumentInPipeline #
You called an extensionless file (a POSIX-style shim, no .exe/.cmd/.ps1) from PowerShell. PowerShell treats it as an unassociated document rather than an executable. Outside a pipeline the same call produces no output and no error at all — this message only appears when you pipe it. See finding 4.
Device or resource busy — rm: cannot remove, mv: cannot move, cp: cannot create regular file #
Another process has the file open. This is MSYS reporting Win32 ERROR_SHARING_VIOLATION as EBUSY. Unlike POSIX, Windows file sharing is opt-in at open time, so delete/rename/overwrite fail while a handle is held — and they fail even when the holder opened the file read-shared, because FileShare.Read does not grant FILE_SHARE_DELETE. See finding 11.
warning: in the working copy of '…', LF will be replaced by CRLF the next time Git touches it #
Not a repo misconfiguration: core.autocrlf=true ships in Git for Windows' system config, so it applies before you set anything. The committed blob stays LF; your working copy gets CRLF on checkout. Confirm the source with git config --show-origin core.autocrlf. See finding 7.
FIND: Parameter format not correct #
You reached DOS find.exe, not GNU find. In PowerShell, bare names can resolve to entirely different programs than in Git Bash — sort is Sort-Object, find is find.exe. See finding 6.
Running cmd /c … from Git Bash prints the Windows banner and hangs at a prompt #
MSYS rewrote your /c as a path before cmd.exe ever saw it, so cmd got no switch and opened interactively. Any /-prefixed argument to a native binary is affected: /test arrives as C:/Program Files/Git/test. Fixes are cmd //c (doubled slash) or MSYS_NO_PATHCONV=1. See finding 10.
git status shows every line of a file changed after adding .gitattributes #
Setting eol=lf does not by itself dirty the tree — the phantom modification appears only once git add --renormalize . runs, and then the whole file shows as changed because stored CRLF bytes are being rewritten to LF. Content did not change; encoding did. See finding 8.
failed to connect to the docker API at npipe:////./pipe/dockerDesktopLinuxEngine #
The Docker CLI is installed but the daemon is not running. This page hit exactly that, which is why the Docker claims are listed as untested rather than repeated as fact.
The failures that print nothing — what to search for instead #
These leave no string to paste into a search engine, which is the reason they survive so long. Check by symptom:
ln -s made a copy, not a link. file yourlink says ASCII text instead of symbolic link to …. Set MSYS=winsymlinks:nativestrict. See finding 1.
- Renaming a file to change only its capitalization destroyed the other file. No error, no prompt. See finding 2.
- The same command name runs a different program depending on the shell. Compare
file $(which npm) in Git Bash against Get-Command npm in PowerShell. See finding 5.
- A committed executable bit does not stick.
core.filemode=false means git never records the change. See finding 9.
Same caveat as the rest of the page: one machine, 2026-08-08, with the toggles listed at the top. If one of these no longer reproduces for you, that is worth an issue.