Skip to content

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 — set PATH explicitly 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 system PATH, 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 doctor runs 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 clean claude doctor does 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.

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.