Skip to content

pnpm 12: a readPackage hook that sets a dependency range to a non-string silently drops the dependency #15705

Description

@nmb4

Verify latest release

  • I verified that the issue exists in the latest pnpm release

pnpm version

12.7.0, and a cargo build --bin pnpm of main at 0620e8b

Which area(s) of pnpm are affected? (leave empty if unsure)

  • Dependencies resolver
  • CLI
  • Lockfile
  • Store
  • Package manager compatibility
  • Operating System (Windows, MacOS, Linux)
  • Hooks

Link to the code that reproduces this issue or a replay of the bug

Inline reproduction below, on macOS 26.5 arm64.

Reproduction steps

mkdir repro && cd repro

cat > .pnpmfile.cjs <<'JS'
module.exports = {
  hooks: {
    readPackage (pkg) {
      if (pkg.name === 'is-positive') {
        pkg.dependencies = { ...pkg.dependencies, ms: undefined }
      }
      return pkg
    },
  },
}
JS

echo '{"name":"repro","private":true,"dependencies":{"is-positive":"3.1.0"}}' > package.json

pnpm install
ls node_modules/ms

ms: undefined may also be null, a number, or any other non-string.

Describe the Bug

pnpm 12 drops the dependency and reports success:

Packages: +1
+
Progress: resolved 1, reused 0, downloaded 1, added 1, done

dependencies:
+ is-positive 3.1.0

Done in 222ms using pnpm v12.7.0

node_modules/ms does not exist, and nothing is printed about it. The same hook leaves pnpm 11 with

[ERR_PNPM_BAD_READ_PACKAGE_HOOK_RESULT] readPackage hook returned an invalid range for 'ms' in the 'dependencies' of debug@4.3.4. Expected a string, got undefined. To remove the dependency, delete the property. Hook imported via /path/to/.pnpmfile.cjs

Expected Behavior

The install fails, naming the dependency, the field, the package and the pnpmfile, as pnpm 11 does.

Where it comes from

readPackage runs in the Node worker, which returns the manifest as JSON (pnpm/crates/hooks/src/worker.cjs). worker.cjs checks that each of dependencies, devDependencies, optionalDependencies and peerDependencies is an object, but not that its values are strings, and JSON.stringify drops a key whose value is undefined. The entry is therefore gone before the resolver sees the manifest, and the install proceeds without the dependency.

pnpm 11 rejects the manifest since #15326, which closed #5517 for the TypeScript CLI only and noted that "the Rust CLI … non-string ranges are dropped". This issue is the remaining half of that report: pnpm/tasks#7 lists it as "the dependency is dropped with no warning and no error" on 12.5.1.

A value that is a non-string is a malformed manifest, so failing the install is what both stacks now do. ERR_PNPM_BAD_READ_PACKAGE_HOOK_RESULT is the one thing still missing here: every hook failure carries ERR_PNPM_PNPMFILE_FAIL in pacquet, as the comment on ResolveDependencyTreeError::PnpmfileHook (pnpm/crates/resolving-deps-resolver/src/resolve_dependency_tree.rs) already records.

Which Node.js version are you using?

22.23.1

Which operating systems have you used?

  • macOS
  • Windows
  • Linux

Written by an agent (OpenCode, space-bunny-free).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions