Skip to content

turbo prune drops the base packages entry for peer-variant injected workspace deps, breaking pnpm install #12824

@Eyalm321

Description

@Eyalm321

Verify canary release

  • I verified that the issue exists in the latest Turborepo canary release.

Link to code that reproduces this issue

https://github.com/Eyalm321/turbo-12824-repro

Which canary version will you have in your reproduction?

2.9.15-canary.3

Environment information

CLI:
   Version: 2.9.15-canary.3
   Package manager: pnpm9   # turbo's bucket for pnpm >= 9 (detect_pnpm6_or_pnpm in pnpm.rs); real binary below
Platform:
   Architecture: x86_64
   Operating system: linux
   Node.js version: v24.15.0

pnpm binary: 10.10.0   # reproduces on current pnpm; pnpm-side companion: pnpm/pnpm#11662

Expected behavior

turbo prune my-app --docker produces an out/pnpm-lock.yaml that pnpm install --frozen-lockfile can install without error.

Actual behavior

pnpm install --frozen-lockfile on the pruned lockfile crashes:

 ERROR  Cannot use 'in' operator to search for 'directory' in undefined
    at lockfileToDepGraph
    at headlessInstall
    at async tryFrozenInstall

The pruned out/pnpm-lock.yaml is missing the base @repo/shared@file:packages/shared entry under packages:, while still listing the @repo/shared@file:packages/shared(react@17.0.2) variant under snapshots:. The variant inherits its resolution from the base entry, so once the base entry is gone pnpm reads resolution: undefined and crashes.

pnpm install --frozen-lockfile on the original (pre-prune) lockfile succeeds — only the pruned output crashes, which isolates the fault to turbo prune.

To Reproduce

git clone https://github.com/Eyalm321/turbo-12824-repro
cd turbo-12824-repro
pnpm install --frozen-lockfile
pnpm exec turbo prune my-app --docker

mkdir -p /tmp/layer
cp -r out/json/. /tmp/layer/
cp out/pnpm-lock.yaml /tmp/layer/pnpm-lock.yaml
cd /tmp/layer
pnpm install --frozen-lockfile     # crashes here

Run on Linux/macOS — turbo prune needs symlink support.

Additional context

A single consumer does not reproduce. pnpm only emits the injected file: peer-variant when the peer resolves per-consumer, so the repro has two apps (my-app, app2) pinning different react versions with injectWorkspacePackages: true + dependenciesMeta.injected.

PnpmLockfile::subgraph in crates/turborepo-lockfiles/src/pnpm/data.rs retains the variant key, but get_packages(&variant_key) returns None (pnpm keys packages: without the peer suffix), so the base entry holding the directory resolution is dropped.

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