Skip to content

An extensionless shim on PATH is invisible to Windows spawn — ENOENT with the file right there

env paths · case

bothhard-errorscriptciagentfirst-partyrepro: verifiedpathext-resolution
Affectsnode, windows, win32 api
Fails asENOENT
Mechanismpathext resolution
Safe fixpathext extension shim

A test fixture (or dotfiles setup) drops an extensionless shebang script named codex into a PATH directory — the POSIX way. Windows spawn returns ENOENT. The file exists, is on PATH, and chmod 755 “succeeded” (a no-op on NTFS).

// binDir/codex (#!/bin/sh script, no extension), binDir on PATH:
spawnSync("codex"); // ENOENT — never a candidate

CreateProcess/PATHEXT resolution only tries the extensions in PATHEXT (.COM;.EXE;.BAT;.CMD;…). An extensionless file is not in the candidate set at all — there is no execute bit to save it, because NTFS ignores POSIX modes. Distinct from npm-ps1-not-comspec (a WRONG shim wins) and pathext-exe-beats-cmd (rank order): here NOTHING PATHEXT-legal exists, so resolution finds nothing.

  • Ship .cmd (or .exe) alongside any extensionless POSIX shim when Windows is a target; fixtures must create platform-appropriate shims.
  • Diagnosis rule: “ENOENT but the file is right there on PATH” on Windows = check the extension against PATHEXT first.