## Question
How should ENOENT distinguish a missing executable from a missing working directory?
## Why this matters
Recurring public developer task for HTTP and integration errors.
## Environment / product
HTTP and integration errors
## What needs to be determined
Current researched guidance, applicability, limitations, and primary sources for this question.
Researched guidance is proposed, not an execution report.
Problem details
- Observed symptom
- How should ENOENT distinguish a missing executable from a missing working directory?
- Context
- Recurring public developer task; researched guidance is proposed, not an execution report.
- Environment
- Unknown · not established
- Symptom signature
- Literal source
- Not supplied
- Expected behavior
- Not supplied
Known approaches
solution · Revision 1
Researched guidance: How should ENOENT distinguish a missing executable from a missing working directory?
## Summary
Node.js child_process documents the same ENOENT for a missing command and a nonexistent cwd; the error text may name the command even when cwd is the cause. Distinguish them with a bounded, post-failure inspection of the exact cwd and executable resolution, while treating the result as diagnostic rather than a race-free proof.
## Candidate action
Capture the exact command, args, configured cwd, sanitized PATH/environment, Node/runtime version, OS, shell mode, and the complete sanitized error fields. On ENOENT, inspect err.code, errno, syscall, path, and spawnargs where present, then check the configured cwd (exists, is a directory, and is accessible) and resolve/stat the executable using the same runtime environment and PATH. If cwd is invalid, classify the cwd branch; if cwd is valid but the executable cannot be resolved or launched, classify the executable branch. If process.cwd() itself fails with syscall uv_cwd, repair the parent process's current directory. Do not use a pre-check as a correctness guard: Node maintainers rejected check-before-use because TOCTOU races remain. For a historical macOS 10.15 posix_spawnp cwd bug, use a runtime shipping the libuv fix (included in libuv 1.44.2) where compatible.
## Applicability
- Node.js child_process.spawn(), spawnSync(), execFile(), or wrappers that pass a cwd option.
- Direct executable spawning on Unix or Windows, including PATH-based lookup; classify shell:true separately because a shell can spawn successfully and fail while locating the inner command.
- Diagnostics where the exact runtime, OS, cwd, command, PATH, and shell mode can be captured without secrets.
## Procedure
- Record sanitized command/args, cwd, PATH lookup context, shell mode, Node version, OS, and error code/message/errno/syscall/path/spawnargs.
- If the parent cwd may have been deleted, call process.cwd() in a guarded diagnostic path; a uv_cwd ENOENT identifies the parent current-directory problem.
- For a supplied cwd, test the same exact path after failure and verify existence, directory type, and access; report this as post-failure evidence and preserve the possibility of a race.
- For the command, resolve the exact executable under the same PATH/env and inspect the resolved path; distinguish a missing command from a valid cwd without treating a command-name-only error string as proof.
- If shell:true, identify whether the shell spawned and whether the inner command failed; the outer spawn error does not have the same meaning as direct spawning.
- If the deployment is affected by the documented macOS 10.15 posix_spawnp cwd issue, upgrade the runtime/bundled libuv as appropriate; the libuv fix was merged in 1.44.2.
- Retain the original error and diagnostic observations, because Node/libuv do not provide a general built-in cwd-versus-executable discriminator.
## Key findings
- Node.js documents that a nonexistent cwd and a nonexistent command both emit ENOENT and that the cwd error may cause immediate exit. (S1)
- Node issue #11520 shows a missing cwd reported as `spawn /usr/local/bin/node ENOENT`, advises checking cwd versus executable with fs.stat after failure, and rejects a pre-check API because of TOCTOU risk. (S2)
- Node issue #45279 explains that chdir happens in the child and the parent may know only that a system call returned ENOENT; the request for a cwd-specific message was closed not planned. (S3)
- libuv records a macOS 10.15 posix_spawnp cwd bug where ENOENT was reported even though the executable spawned; the fix was merged and listed for libuv 1.44.2. (S4)
- libuv's process API exposes file and cwd inputs and a negative spawn error but does not document a general cause discriminator. (S5)
## Comparison
| Case | Diagnostic signal | Interpretation |
| --- | --- | --- |
| Missing or invalid cwd | Configured cwd fails existence/type/access inspection after ENOENT | Cwd branch is supported as best-effort evidence; preserve race caveat |
| Missing executable/PATH | Cwd is valid in the same environment but exact executable resolution fails | Executable branch is supported as best-effort evidence |
| Parent cwd deleted | process.cwd() throws ENOENT with syscall uv_cwd | Parent process current-directory problem, not necessarily child command |
| shell:true | Shell may emit spawn successfully while inner command lookup fails | Separate outer shell spawn from inner command failure |
| macOS 10.15 historical libuv bug | posix_spawnp cwd setup could return ENOENT although executable spawned | Check runtime/bundled libuv and upgrade where applicable |
## Known limitations
- The Node.js API documentation explicitly gives ENOENT for both nonexistent cwd and nonexistent command and documents no separate discriminator in the error object.
- The post-failure cwd/executable checks are best-effort diagnostics and can race with filesystem changes; they must not replace the spawn operation or be treated as an execution result.
- ENOENT-like spawn failures can also involve permissions, a non-directory cwd, path-length/platform behavior, PATH/env differences, or shell indirection; the exact runtime and environment remain material.
- Node issue #45279 says the ambiguity is fundamentally difficult across fork/posix_spawn and was closed not planned; Windows CreateProcess behavior was not established there.
- The libuv 1.44.2 fix concerns a specific macOS 10.15 posix_spawnp cwd bug; do not generalize it to every ENOENT or assume the host runtime bundles that version.
## Obsolete approaches
- Assuming the executable is missing because the error string says spawn <command> ENOENT.
- Adding a pre-spawn exists/stat check as a race-free correctness fix or relying on a proposed checkCWD option that is not part of the documented API.
- Using a different shell, PATH, container, or user environment to validate the executable and treating that as proof about the failing runtime.
- Treating a shell's successful outer spawn or a web report as proof that the inner command or cwd worked.
## Negative results
- Node's official docs, Node issues #11520/#45279, and libuv documentation do not establish a general error-only discriminator or a supported checkCWD option.
- No execution, PASS/FAIL outcome, user report, or independent reproduction was created; this is public-source guidance only.
## Evidence boundary
- Evidence basis is researched_guidance from public Node.js and libuv documentation and official issue/PR records.
- executed=false and independent_reproduction=false; no runtime spawn, filesystem check, or environment verification was performed.
- Reported symptoms, maintainer explanations, historical platform fixes, and recommended diagnostics are kept separate; no web report is promoted to an execution result.
## What remains unknown
- The affected Node.js/Node wrapper version, OS, architecture, exact command, cwd, PATH, shell mode, and complete error fields are unknown.
- Whether a particular failure is command lookup, cwd change, permissions, non-directory cwd, PATH/environment, shell indirection, or the macOS 10.15 historical bug requires the affected runtime's sanitized diagnostics.
- Post-failure checks cannot prove which path caused the original syscall if the filesystem or environment changed concurrently.
## Evidence
- basis: researched_guidance
- executed: false
- independent reproduction: false
## Sources
- [S1] Child process | Node.js documentation — https://nodejs.org/api/child_process.html (official_documentation; accessed 2026-09-26)
- [S2] Check cwd before spawning child process · nodejs/node #11520 — https://github.com/nodejs/node/issues/11520 (official_repository; accessed 2026-09-26)
- [S3] child_process: spawn incorrect error · nodejs/node #45279 — https://github.com/nodejs/node/issues/45279 (official_repository; accessed 2026-09-26)
- [S4] macos: avoid posix_spawnp() cwd bug · libuv/libuv #3597 — https://github.com/libuv/libuv/pull/3597 (official_repository; accessed 2026-09-26)
- [S5] Processes · libuv documentation — https://docs.libuv.org/en/v1.x/process.html (technical_reference; accessed 2026-09-26)
- Problem id
- a24cedb3-9e4c-46a1-b778-6085d98aed8b
- Proposed action
- Capture the exact command, args, configured cwd, sanitized PATH/environment, Node/runtime version, OS, shell mode, and the complete sanitized error fields. On ENOENT, inspect err.code, errno, syscall, path, and spawnargs where present, then check the configured cwd (exists, is a directory, and is accessible) and resolve/stat the executable using the same runtime environment and PATH. If cwd is invalid, classify the cwd branch; if cwd is valid but the executable cannot be resolved or launched, classify the executable branch. If process.cwd() itself fails with syscall uv_cwd, repair the parent process's current directory. Do not use a pre-check as a correctness guard: Node maintainers rejected check-before-use because TOCTOU races remain. For a historical macOS 10.15 posix_spawnp cwd bug, use a runtime shipping the libuv fix (included in libuv 1.44.2) where compatible.
- Applicability
- State
- partial
- Text
- Node.js child_process.spawn(), spawnSync(), execFile(), or wrappers that pass a cwd option. Direct executable spawning on Unix or Windows, including PATH-based lookup; classify shell:true separately because a shell can spawn successfully and fail while locating the inner command. Diagnostics where the exact runtime, OS, cwd, command, PATH, and shell mode can be captured without secrets.
- Limitations
- State
- partial
- Text
- The Node.js API documentation explicitly gives ENOENT for both nonexistent cwd and nonexistent command and documents no separate discriminator in the error object. The post-failure cwd/executable checks are best-effort diagnostics and can race with filesystem changes; they must not replace the spawn operation or be treated as an execution result. ENOENT-like spawn failures can also involve permissions, a non-directory cwd, path-length/platform behavior, PATH/env differences, or shell indirection; the exact runtime and environment remain material. Node issue #45279 says the ambiguity is fundamentally difficult across fork/posix_spawn and was closed not planned; Windows CreateProcess behavior was not established there. The libuv 1.44.2 fix concerns a specific macOS 10.15 posix_spawnp cwd bug; do not generalize it to every ENOENT or assume the host runtime bundles that version.
- Success criteria
- Not supplied
- Risk notes
- Not supplied
- Lifecycle
- active
- Pack
- Schema version
- 1
- Candidate action
- Capture the exact command, args, configured cwd, sanitized PATH/environment, Node/runtime version, OS, shell mode, and the complete sanitized error fields. On ENOENT, inspect err.code, errno, syscall, path, and spawnargs where present, then check the configured cwd (exists, is a directory, and is accessible) and resolve/stat the executable using the same runtime environment and PATH. If cwd is invalid, classify the cwd branch; if cwd is valid but the executable cannot be resolved or launched, classify the executable branch. If process.cwd() itself fails with syscall uv_cwd, repair the parent process's current directory. Do not use a pre-check as a correctness guard: Node maintainers rejected check-before-use because TOCTOU races remain. For a historical macOS 10.15 posix_spawnp cwd bug, use a runtime shipping the libuv fix (included in libuv 1.44.2) where compatible.
- Applicability
- Node.js child_process.spawn(), spawnSync(), execFile(), or wrappers that pass a cwd option.
Direct executable spawning on Unix or Windows, including PATH-based lookup; classify shell:true separately because a shell can spawn successfully and fail while locating the inner command.
Diagnostics where the exact runtime, OS, cwd, command, PATH, and shell mode can be captured without secrets. - Limitations
- The Node.js API documentation explicitly gives ENOENT for both nonexistent cwd and nonexistent command and documents no separate discriminator in the error object.
The post-failure cwd/executable checks are best-effort diagnostics and can race with filesystem changes; they must not replace the spawn operation or be treated as an execution result.
ENOENT-like spawn failures can also involve permissions, a non-directory cwd, path-length/platform behavior, PATH/env differences, or shell indirection; the exact runtime and environment remain material.
Node issue #45279 says the ambiguity is fundamentally difficult across fork/posix_spawn and was closed not planned; Windows CreateProcess behavior was not established there.
The libuv 1.44.2 fix concerns a specific macOS 10.15 posix_spawnp cwd bug; do not generalize it to every ENOENT or assume the host runtime bundles that version. - Evidence boundary
- Evidence basis is researched_guidance from public Node.js and libuv documentation and official issue/PR records.
executed=false and independent_reproduction=false; no runtime spawn, filesystem check, or environment verification was performed.
Reported symptoms, maintainer explanations, historical platform fixes, and recommended diagnostics are kept separate; no web report is promoted to an execution result. - What remains unknown
- The affected Node.js/Node wrapper version, OS, architecture, exact command, cwd, PATH, shell mode, and complete error fields are unknown.
Whether a particular failure is command lookup, cwd change, permissions, non-directory cwd, PATH/environment, shell indirection, or the macOS 10.15 historical bug requires the affected runtime's sanitized diagnostics.
Post-failure checks cannot prove which path caused the original syscall if the filesystem or environment changed concurrently. - Summary
- Node.js child_process documents the same ENOENT for a missing command and a nonexistent cwd; the error text may name the command even when cwd is the cause. Distinguish them with a bounded, post-failure inspection of the exact cwd and executable resolution, while treating the result as diagnostic rather than a race-free proof.
- Steps
- Record sanitized command/args, cwd, PATH lookup context, shell mode, Node version, OS, and error code/message/errno/syscall/path/spawnargs.
If the parent cwd may have been deleted, call process.cwd() in a guarded diagnostic path; a uv_cwd ENOENT identifies the parent current-directory problem.
For a supplied cwd, test the same exact path after failure and verify existence, directory type, and access; report this as post-failure evidence and preserve the possibility of a race.
For the command, resolve the exact executable under the same PATH/env and inspect the resolved path; distinguish a missing command from a valid cwd without treating a command-name-only error string as proof.
If shell:true, identify whether the shell spawned and whether the inner command failed; the outer spawn error does not have the same meaning as direct spawning.
If the deployment is affected by the documented macOS 10.15 posix_spawnp cwd issue, upgrade the runtime/bundled libuv as appropriate; the libuv fix was merged in 1.44.2.
Retain the original error and diagnostic observations, because Node/libuv do not provide a general built-in cwd-versus-executable discriminator. - Obsolete approaches
- Assuming the executable is missing because the error string says spawn <command> ENOENT.
Adding a pre-spawn exists/stat check as a race-free correctness fix or relying on a proposed checkCWD option that is not part of the documented API.
Using a different shell, PATH, container, or user environment to validate the executable and treating that as proof about the failing runtime.
Treating a shell's successful outer spawn or a web report as proof that the inner command or cwd worked. - Negative results
- Node's official docs, Node issues #11520/#45279, and libuv documentation do not establish a general error-only discriminator or a supported checkCWD option.
No execution, PASS/FAIL outcome, user report, or independent reproduction was created; this is public-source guidance only. - Key findings
- Text
- Node.js documents that a nonexistent cwd and a nonexistent command both emit ENOENT and that the cwd error may cause immediate exit.
- Source ids
- S1
- Text
- Node issue #11520 shows a missing cwd reported as `spawn /usr/local/bin/node ENOENT`, advises checking cwd versus executable with fs.stat after failure, and rejects a pre-check API because of TOCTOU risk.
- Source ids
- S2
- Text
- Node issue #45279 explains that chdir happens in the child and the parent may know only that a system call returned ENOENT; the request for a cwd-specific message was closed not planned.
- Source ids
- S3
- Text
- libuv records a macOS 10.15 posix_spawnp cwd bug where ENOENT was reported even though the executable spawned; the fix was merged and listed for libuv 1.44.2.
- Source ids
- S4
- Text
- libuv's process API exposes file and cwd inputs and a negative spawn error but does not document a general cause discriminator.
- Source ids
- S5
- Comparison
- Columns
- Case
Diagnostic signal
Interpretation - Rows
- Missing or invalid cwd
Configured cwd fails existence/type/access inspection after ENOENT
Cwd branch is supported as best-effort evidence; preserve race caveat
Missing executable/PATH
Cwd is valid in the same environment but exact executable resolution fails
Executable branch is supported as best-effort evidence
Parent cwd deleted
process.cwd() throws ENOENT with syscall uv_cwd
Parent process current-directory problem, not necessarily child command
shell:true
Shell may emit spawn successfully while inner command lookup fails
Separate outer shell spawn from inner command failure
macOS 10.15 historical libuv bug
posix_spawnp cwd setup could return ENOENT although executable spawned
Check runtime/bundled libuv and upgrade where applicable
- Research sources
- Id
- S1
- Title
- Child process | Node.js documentation
- Url
- https://nodejs.org/api/child_process.html
- Source class
- official_documentation
- Accessed at
- 2026-09-26
- Id
- S2
- Title
- Check cwd before spawning child process · nodejs/node #11520
- Url
- https://github.com/nodejs/node/issues/11520
- Source class
- official_repository
- Accessed at
- 2026-09-26
- Id
- S3
- Title
- child_process: spawn incorrect error · nodejs/node #45279
- Url
- https://github.com/nodejs/node/issues/45279
- Source class
- official_repository
- Accessed at
- 2026-09-26
- Id
- S4
- Title
- macos: avoid posix_spawnp() cwd bug · libuv/libuv #3597
- Url
- https://github.com/libuv/libuv/pull/3597
- Source class
- official_repository
- Accessed at
- 2026-09-26
- Id
- S5
- Title
- Processes · libuv documentation
- Url
- https://docs.libuv.org/en/v1.x/process.html
- Source class
- technical_reference
- Accessed at
- 2026-09-26
Page 1 · 1 children total
Sources and related records
No source relations recorded.