Error: Failed to spawn Claude Code process: spawn node ENOENT
A process launch failed before anything ran. Something tried to start node as a
child process, the operating system could not find a program by that name, and
the launch returned ENOENT. Nothing here touched the network, so this is not an
authentication problem, not a quota or rate limit, and not the API being down.
Claude Code itself never started — which is why this message does not come from
Claude Code at all, and why none of its diagnostics or retries were ever in play.
The word after ENOENT tells you which kind of missing you have
ENOENT is one error code covering two very different faults, and the payload
separates them cleanly.
A bare program name — spawn node ENOENT — is a lookup failure. The
operating system was asked to run a program called node, searched the PATH it
was given, and found nothing. The file that is “missing” is an executable that
was never located, not a file you can point at.
An absolute path after ENOENT is a path that genuinely is not there. That
is the other ENOENT people hit with this tool:
scandir on a config directory
prints the full directory it looked in, and the fix lives in configuration rather
than in program resolution. If the string in front of you names a directory, that
page is yours and this one is not.
So the question is never “what does ENOENT mean”. It is which PATH was
searched, and the answer is almost never the one you are looking at.
The process that failed is not the process you tested
Here is the reasonable, expensive assumption: claude works fine in my
terminal, so node is installed, so this must be a bug in the tool. The first
clause is true and the conclusion does not follow.
A child process inherits its environment from its parent. The spawn that failed
happened inside something else — an editor extension, a background service, an
SDK application, a container entrypoint, a CI step, a cron job — and it searched
that process’s PATH. Your interactive shell’s PATH was assembled by your
shell startup files. Nothing else on the machine reads those files.
This matters enormously if a version manager owns your Node installation. Tools
of that kind work by putting a per-user directory on PATH from a line in a
shell rc file, and sometimes by installing a shell function that resolves the
interpreter at call time. Neither survives outside an interactive shell. An
application launched from a desktop icon, a unit started by an init system, a
scheduled task, and a process inside a container all begin life with a PATH
that no rc file ever touched — so node is genuinely absent from their world
while being obviously present in yours.
The single most useful thing you can do is stop testing in your terminal and
print PATH from inside the process that actually failed. Everything below is
a variation on that.
Spawning is stricter than typing
A second reason your terminal is a bad witness: launching a child process does not go through a shell unless the caller asks for one. That removes several layers you rely on without noticing.
Shell aliases do not apply. Shell functions do not apply. Anything exported by an
rc file at shell startup does not apply, because no shell started. The runtime
resolves the executable name itself, against the inherited PATH and nothing
else.
On Windows the same strictness has a specific consequence: a bare node means an
executable Windows will accept as one. A version manager that satisfies your
prompt through a shim script can leave a shell-less spawn with nothing to
execute, even though typing node --version at the same prompt prints a version
happily. If your failure is Windows-only, test by launching the wrapper without a
shell rather than by typing the command again.
Is retrying useful?
No. PATH does not change between two attempts of the same process, so the
second launch searches the same directories and fails at the same instant.
There is a second, sharper reason, and it is the one worth remembering. Claude
Code retries transient failures internally — up to ten attempts with exponential
backoff, the CLAUDE_CODE_MAX_RETRIES default — and prints a Retrying in Ns · attempt x/y line while it does. None of that machinery exists here, because it
lives inside a process that never started. There is no session, so there is no
/doctor, no /status, no /context, and no automatic recovery of any kind.
The only actor in this failure is whatever tried to do the spawning.
The same reasoning explains a neighbouring message: Error: Claude Code process exited with code 1 is a wrapper error printed by an editor or an SDK
application, not by Claude Code, and the exit code by itself identifies nothing.
Treat both strings as the wrapper reporting on a child, and go read the
wrapper’s own output for the detail.
Four checks that find the empty PATH
Run them in order; each one either indicts an environment or clears it.
What does the failing process see? Log PATH from the code that performs the
spawn, or from the container’s entrypoint, or from the service unit — before the
spawn, in the same process. This is the only measurement that counts. Comparing
it against your shell’s PATH usually ends the investigation in one diff.
Does it work when the same wrapper is launched from a terminal? Start your application from an interactive shell instead of from a desktop launcher, a service manager, or a scheduler. If it succeeds there and fails when launched the other way, you have confirmed an environment-inheritance problem and can stop looking at the installation.
Who owns node on this machine? Run command -v node (or where node on
Windows) in a terminal where it works. If the answer sits under a per-user
version-manager directory rather than a system location, you have found why a
non-interactive process cannot see it.
In a container, whose environment did you inspect? An interactive
docker exec shell can have a different environment from the image’s entrypoint.
Check the PATH baked into the image and the one the entrypoint runs with, not
the one you get after attaching. A build that installs Node in one layer and runs
the application under a different user or a slimmer final image is the usual
shape of this failure.
Fixes, attached to the check that selected them
- The failing process has a short
PATH— setPATHexplicitly where that process is defined: the service unit, the scheduled task, the container image, the CI job configuration. Do not fix this by editing a shell profile; the process that fails does not read one. - Launching from a terminal works, launching from the app does not — give the application an environment. Either start it from a shell that already has one, or configure the environment in the application’s own settings, whichever it supports.
- A version manager owns
node— the durable fix is to make the interpreter reachable without a shell: install Node somewhere on the systemPATH, or configure the caller with an absolute interpreter path so no lookup is needed. A shim that only works interactively will keep breaking every non-interactive caller you add. - It is a container — install and expose Node in the image, verify with the entrypoint’s environment rather than an attached shell, and rebuild. A change made inside a running container is gone on the next start.
- You are not sure the CLI is installed correctly at all —
claude doctorruns read-only installation and settings diagnostics from the shell without starting a session. Useful, with one caveat that matters here: it reports on the environment you ran it in, which is exactly the environment that is not failing. A cleanclaude doctordoes not clear the wrapper.
One boundary this page will not cross: install layout, package name and how the
installer sets up PATH are things to take from the current official
installation documentation, not from a write-up. Those details move, and a stale
instruction here would send you to create a directory or edit a profile that no
longer matters.
Confirming the fix
Restart the thing that failed — the service, the container, the editor, the CI job — so that a changed environment is genuinely inherited. Editing an environment in one window and testing in another is the most common reason a correct fix appears not to work.
Then require three things before calling it done. The spawn must succeed twice
in a row from the same non-interactive context, because a single success after
an environment change can just mean you happened to test in a process that
already had the value. A Claude Code session must actually start and produce
output, not merely fail to print this error. And the PATH you log from inside
the failing process must now contain the directory holding the interpreter — that
is the falsifiable part, and it is the only one that survives the next reboot
being reasoned about.
Related errors
If your missing path is inside a container or a WSL filesystem rather than on the
host disk, check the mount before you check PATH: a share that never mounted
makes an entire tree look absent, and both
Plan9 mount failed: invalid argument
and
failed to ensure virtiofs mount: Plan9 mount failed: bad address
produce that illusion.
If the wrapper starts the process but something downstream rejects a version string, ERROR Invalid Version is the other failure where the useful question is which program produced this line, not what the line says.
When you cannot yet tell which layer you are on — program resolution, filesystem, container mount, or the API — the AI coding error triage tool sorts these by the signals above so the next ten minutes go to the right one.