vercel/turborepo · error · NpxTurboProcess

Failed to execute `turbo` via `npx`.

Error message

Failed to execute `turbo` via `npx`.

What it means

The shim's fallback path runs `npx turbo` when no usable local turbo exists; the spawn itself (runtime.child_spawner.spawn, run.rs:524 region) failed at the OS level and is wrapped as Error::NpxTurboProcess with the io::Error as #[source] (run.rs:114-115). Note the distinction: `which` failing to find npx is a separate Error::Which — this error means npx was found but exec'ing it failed.

Source

Thrown at crates/turborepo-shim/src/run.rs:524

    raw_args.append(&mut shim_args.forwarded_args);

    raw_args
}

fn spawn_child_turbo<R, C, S, V>(
    runtime: &ShimRuntime<R, C, S, V>,
    command: process::Command,
    err: fn(std::io::Error) -> Error,
) -> ShimResult<R::Error>
where
    R: TurboRunner,
    C: ConfigProvider,
    S: ChildSpawner,
    V: VersionProvider,
{
    let child: Arc<SharedChild> = match runtime.child_spawner.spawn(command) {
        Ok(child) => child,
        Err(e) => return ShimResult::ShimError(err(e)),
    };

    // The child turbo process shares our process group and will receive
    // SIGINT directly from the kernel when the user presses Ctrl+C.
    // Ignore SIGINT in the shim *after* spawning so the child inherits
    // the default disposition (and can register its own handler), while
    // the shim stays alive to collect the child's exit status.
    #[cfg(unix)]
    unsafe {
        libc::signal(libc::SIGINT, libc::SIG_IGN);
    }

    let exit_status = match child.wait() {
        Ok(status) => status,
        Err(e) => return ShimResult::ShimError(err(e)),
    };
    let exit_code = exit_status.code().unwrap_or_else(|| {
        debug!("child turbo failed to report exit code");

View on GitHub (pinned to f9245100cf)

Solutions

  1. Run `npx turbo --version` manually and read the underlying errno — that is the #[source] of this error
  2. Reinstall Node/npm to regenerate the npx shim; check `head -1 $(which npx)` points at an existing node
  3. Install turbo locally in the repo so the shim uses the local binary and never needs the npx fallback
  4. Ensure PATH visible to spawned children includes the node bin directory

Example fix

# diagnose
npx turbo --version
head -1 "$(command -v npx)"
# fix: regenerate shims / prefer local install
npm install -g npm
pnpm add -w -D turbo
Defensive patterns

Strategy: fallback

Validate before calling

// verify npx actually executes before relying on the fallback
let ok = std::process::Command::new("npx").arg("--version").status().map(|s| s.success()).unwrap_or(false);
if !ok { install_turbo_locally(); }

Try / catch

// prefer local turbo; only fall back to npx, and surface the source errno
match run_local_turbo() {
    ShimResult::ShimError(Error::LocalTurboProcess(_)) | Err(_) => {
        match run_via_npx() {
            ShimResult::ShimError(Error::NpxTurboProcess(src)) => handle_errno(src),
            r => r,
        }
    }
    r => r,
}

Prevention

When it happens

Trigger: Spawning `npx turbo` fails with EACCES (npx shim not executable), ENOENT/ENOEXEC (Node was upgraded or moved so the shim's shebang `#!/usr/bin/env node` no longer resolves), or resource exhaustion (EMFILE).

Common situations: Node upgraded via nvm/system package and stale npm shims left behind Permissions broken on the global bin dir after a manual copy Running in minimal containers where env/node is absent from the spawn environment

Related errors


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/a44d4f3a2a5efd3d. Report an issue: GitHub.