Skip to content

devEngines.packageManager: packageManagerDependencies not written to lockfile without onFail, and not cleaned up when onFail is removed #11674

@jiridanek

Description

@jiridanek

Verify latest release

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

pnpm version

11.1.2

Which area(s) of pnpm are affected?

Lockfile, Package manager compatibility

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

(steps below are self-contained)

Reproduction steps

Part 1: packageManagerDependencies not written without onFail

mkdir /tmp/test-devengines && cd /tmp/test-devengines
cat > package.json << 'EOF'
{
  "private": true,
  "devEngines": {
    "packageManager": {
      "name": "pnpm",
      "version": ">=11.0.0"
    }
  },
  "dependencies": {
    "is-odd": "^3.0.1"
  }
}
EOF
cat > pnpm-workspace.yaml << 'EOF'
---
EOF
pnpm install
head -20 pnpm-lock.yaml
# Expected: packageManagerDependencies with resolved version
# Actual: no packageManagerDependencies at all

Part 2: Adding onFail: "download" triggers the write

cat > package.json << 'EOF'
{
  "private": true,
  "devEngines": {
    "packageManager": {
      "name": "pnpm",
      "version": ">=11.0.0",
      "onFail": "download"
    }
  },
  "dependencies": {
    "is-odd": "^3.0.1"
  }
}
EOF
rm -rf node_modules
pnpm install
head -20 pnpm-lock.yaml
# Now packageManagerDependencies appears with @pnpm/exe and platform-specific entries

Part 3: Removing onFail does NOT clean up

cat > package.json << 'EOF'
{
  "private": true,
  "devEngines": {
    "packageManager": {
      "name": "pnpm",
      "version": ">=11.0.0"
    }
  },
  "dependencies": {
    "is-odd": "^3.0.1"
  }
}
EOF
rm -rf node_modules
pnpm install
head -20 pnpm-lock.yaml
# Expected: packageManagerDependencies removed (onFail no longer set)
# Actual: packageManagerDependencies and all @pnpm/exe entries still present

Describe the Bug

Two related issues with devEngines.packageManager and lockfile behavior:

  1. Without onFail: pnpm install does not write packageManagerDependencies to the lockfile at all, even though the docs say "The resolved version is stored in pnpm-lock.yaml" unconditionally. The user has to discover through trial-and-error that onFail: "download" is the trigger.

  2. Removing onFail after it was set: The packageManagerDependencies entries (including ~7 @pnpm/* platform-specific binary packages) remain in the lockfile indefinitely. There's no way to clean them up short of deleting the lockfile and regenerating.

This creates a confusing experience: the feature appears broken without onFail, then once you add onFail to debug it, you get 200+ extra lines in your lockfile that you can't undo.

Expected Behavior

  1. devEngines.packageManager should write packageManagerDependencies to the lockfile regardless of whether onFail is set. The onFail field controls runtime behavior (what to do on mismatch), not whether the resolution is recorded.

  2. If onFail: "download" is removed, pnpm install should prune the @pnpm/exe entries from the lockfile since the download mechanism is no longer active and those entries serve no purpose.

Which Node.js version are you using?

v26.0.0

Which operating systems have you used?

  • macOS
  • Windows
  • Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions