pulumi/pulumi · error

find npm: %w

Error message

find npm: %w

What it means

Pack resolves npm once at the start of the language host's Pack method via executable.FindExecutable("npm") (sdk/nodejs/cmd/pulumi-language-nodejs/main.go:1983). If no npm binary can be located on PATH (or via nodeenv paths), the error is wrapped as "find npm: %w" and packing aborts before reading package.json.

Source

Thrown at sdk/nodejs/cmd/pulumi-language-nodejs/main.go:1983

func readPackageJSON(packageJSONPath string) (map[string]any, error) {
	packageJSONData, err := os.ReadFile(packageJSONPath)
	if err != nil {
		return nil, fmt.Errorf("read package.json: %w", err)
	}
	var packageJSON map[string]any
	err = json.Unmarshal(packageJSONData, &packageJSON)
	if err != nil {
		return nil, fmt.Errorf("unmarshal package.json: %w", err)
	}
	return packageJSON, nil
}

func (host *nodeLanguageHost) Pack(ctx context.Context, req *pulumirpc.PackRequest) (*pulumirpc.PackResponse, error) {
	// Verify npm exists and is set up: npm, user login
	npm, err := executable.FindExecutable("npm")
	if err != nil {
		return nil, fmt.Errorf("find npm: %w", err)
	}

	// Annoyingly the engine will call Pack for the core SDK which is not setup in at all the same way as the
	// generated sdks, so we have to detect that and do a big branch to pack it totally differently.
	packageJSON, err := readPackageJSON(filepath.Join(req.PackageDirectory, "package.json"))
	if err != nil {
		return nil, err
	}

	// TODO: We're just going to write to stderr for now, but we should probably have a way to return this
	// directly to the engine hosting us. That's a bit awkward because we need the subprocesses to write to
	// this as well, and the host side of this pipe might be a tty and if it is we want the subprocesses to
	// think they're connected to a tty as well. We've solved this problem before in two slightly different
	// ways for InstallDependencies and RunPlugin, it would be good to come up with a clean way to do this for
	// all these cases.
	writeString := func(s string) error {
		_, err = os.Stderr.Write([]byte(s))
		return err

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Install Node.js (which bundles npm) in the environment running pulumi, e.g. via nvm, volta, mise, or the OS package manager
  2. Ensure the directory containing npm is on PATH for the shell/process invoking pulumi: 'which npm' must succeed
  3. In CI, use a setup-node/setup step before running pulumi commands
  4. If using nvm, confirm the default alias exists ('nvm alias default') so non-interactive shells resolve node/npm
  5. As a workaround, symlink npm into a directory already on PATH

Example fix

// before
$ pulumi package pack-sdk nodejs ./provider
error: find npm: exec: "npm": executable file not found in $PATH
// after
$ export PATH="$HOME/.nvm/versions/node/v20.11.0/bin:$PATH"
$ which npm
$ pulumi package pack-sdk nodejs ./provider
Defensive patterns

Strategy: validation

Validate before calling

if ! command -v npm >/dev/null 2>&1; then
  echo "npm not found on PATH"; exit 1
fi

Try / catch

try {
  child_process.execSync('npm --version');
} catch (err) {
  console.error('npm is not available on PATH; install Node.js first');
  process.exit(1);
}

Prevention

When it happens

Trigger: Calling Pack (e.g. 'pulumi package pack-sdk' or 'pulumi plugin install' flows that build a Node package) on a machine where the 'npm' executable is not on PATH and not in the nodeenv/npm shims the host checks.

Common situations: Node.js not installed in the environment running the pulumi CLI; npm not installed despite node being present (e.g. custom node builds); PATH stripped of nvm/volta/n dirs in CI or non-interactive shells; mise/asdf shim directories missing.

Related errors


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/9ee4b5cba348e586. Report an issue: GitHub.