OpusMill

Why there is a C:\tmp folder on your Windows machine

Verified on Windows 11, Node 24.20.0 · OpusMill

> path.resolve("/tmp/cache.json")
'C:\tmp\cache.json'

> fs.writeFileSync("/tmp/cache.json", data)
undefined    ← no error. it wrote the file.
The fix

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 meantUse
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:

Background: I scanned the 600 most-downloaded npm CLI packages — 17.4% have a package.json script that cannot run on Windows.