Why there is a C:\tmp folder on your Windows machine
Verified on Windows 11, Node 24.20.0 · OpusMill
'C:\tmp\cache.json'
> fs.writeFileSync("/tmp/cache.json", data)
undefined ← no error. it wrote the file.
A program hardcoded a POSIX path like /tmp/cache.json. On
Windows a leading slash is not an absolute path — it is a path
relative to the current drive — so it resolved
to C:\tmp\cache.json and the file was written there.
Nothing failed. There is no error to search for, which is why this survives in shipped software for years.
In your own code: use
os.tmpdir() for temporary files,
os.homedir() for the user's home, or
env-paths
for config and cache directories. Never hardcode a POSIX root.
What is actually happening
On Linux and macOS, / is the root of the one filesystem.
On Windows there is no single root — there is one per drive
letter. A path that begins with a slash and no drive letter is
therefore incomplete, and Windows completes it with whichever
drive is current. Node does the same:
> path.resolve("/tmp") 'C:\tmp'
> path.resolve("/tmp/myapp/cache.json") 'C:\tmp\myapp\cache.json'
> path.resolve("/var/log/app.log") 'C:\var\log\app.log'
> path.resolve("/etc/hosts") 'C:\etc\hosts'
So this is not only about /tmp. Every hardcoded POSIX root
gets the same treatment — /var, /etc,
/usr, /opt, /home all quietly
become directories on your C: drive.
It is the current drive, not always C:
This is the part that surprises people, and it is the reason the bug can look intermittent. The drive is whichever one the process is running on, so the same line of code resolves to different places depending on where you started it:
cwd C:\Users\someone → /tmp/x resolves to C:\tmp\x cwd D:\projects\app → /tmp/x resolves to D:\tmp\x cwd E:\ → /tmp/x resolves to E:\tmp\x
If you keep your work on a second drive and your tooling runs from C:, two processes that think they are sharing a directory are not sharing anything. One writes a lock file or a cache entry, the other looks for it on a different drive and finds nothing. That reads as a race condition, and it is not one.
C:\tmp is not your temp directory
The most common intent behind /tmp is “somewhere
scratch that gets cleaned up”. C:\tmp is neither:
> os.tmpdir() 'C:\Users\you\AppData\Local\Temp'
> path.resolve("/tmp") 'C:\tmp'
same directory? false
Windows cleans %TEMP% and
C:\Windows\Temp. C:\tmp is not a location
Windows knows about, so nothing ever clears it. It
accumulates for as long as the machine lives.
On the Windows machine this page was written on,
C:\tmp holds 263 entries — build
artefacts, sourcemaps, logs, lock files and archives, left by a range of
unrelated tools over months. Not one of them reported an error. I added
one of those entries myself, from my own deploy script, while writing
this page.
Why nothing tells you
The failure modes people expect — a thrown error, a non-zero exit — do not happen, because from Windows' point of view nothing went wrong. You asked for a path, it was a valid path, it was created:
fs.mkdirSync("/tmp/myapp", { recursive: true })
fs.writeFileSync("/tmp/myapp/state.json", data)
RESULT: succeeded -- it silently created C:\tmp\myapp
The one case that does throw is a bare
writeFileSync into a directory that does not exist yet,
which gives you ENOENT naming a path with
C:\tmp in it. That is the lucky version. The unlucky
version is recursive: true, which creates the wrong
directory for you and returns cleanly.
Which fix to use
| What you meant | Use |
|---|---|
| Scratch space that gets cleaned up | os.tmpdir(), joined with path.join. For a unique directory, fs.mkdtempSync(path.join(os.tmpdir(), "myapp-")). |
~ or /home/user |
os.homedir(). |
| Config, cache or data that persists | env-paths. It returns the right directory per platform — %APPDATA% on Windows, ~/.config on Linux, ~/Library on macOS — instead of one wrong answer everywhere. |
| A path next to your own source files | path.join(__dirname, ...), never a string with slashes in it. |
/etc/ or /usr/ system paths |
There is no Windows equivalent. Branch on process.platform and do something else, or say plainly that the feature is POSIX-only. |
One thing that is not a fix: creating C:\tmp and
moving on. It works on your machine, it silently breaks for anyone
whose work lives on another drive, and the directory is never cleaned.
Check your own project for the rest of this class of bug.
Paste your code or your package.json into
the browser checker — nothing is uploaded,
it runs on your machine — or run
npx github:Hackierz/winbreak over the whole repository.
The rule for this one is hardcoded-posix-path.
Other errors in the same family:
- The fix for 'NODE_ENV' is not recognized
- The fix for 'rm' is not recognized
- The fix for 'C:\Program' is not recognized
- The fix for spawn EINVAL on a .cmd
- The fix for ENOENT on node_modules/.bin
- Path too long on Windows
- EPERM on Windows
- Cannot find module, but only in CI
Background: I scanned the 600 most-downloaded
npm CLI packages — 17.4% have a package.json
script that cannot run on Windows.