Repository navigation
Description
Verify latest release
- I verified that the issue exists in the latest pnpm release
pnpm version
12.14.0
Which area(s) of pnpm are affected? (leave empty if unsure)
No response
Link to the code that reproduces this issue or a replay of the bug
No response
Reproduction steps
Save as repro.mjs, put pnpm 12.14.0 on PATH, and run node repro.mjs. The script creates four fresh temporary workspaces, uses only local packages, runs offline, and disables lifecycle scripts. Optionally pass the pnpm executable as the first argument.
import { mkdtempSync, mkdirSync, writeFileSync, readlinkSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { spawnSync } from 'node:child_process';
const pnpm = process.argv[2] || 'pnpm';
const base = mkdtempSync(join(tmpdir(), 'pnpm-relative-workspace-'));
const run = (command, args, cwd) => {
const result = spawnSync(command, args, {
cwd, encoding: 'utf8', env: { ...process.env, CI: 'true', NO_COLOR: '1' },
});
if (result.error) throw result.error;
return result;
};
console.log('pnpm:', run(pnpm, ['--version'], base).stdout.trim(), 'Node:', process.version);
for (const injected of [false, true]) {
for (const protocol of ['workspace', 'file']) {
const root = join(base, `${protocol}-${injected}`);
const consumer = join(root, 'packages/deep/consumer');
const target = join(root, 'packages/target');
mkdirSync(consumer, { recursive: true });
mkdirSync(target, { recursive: true });
const json = (path, value) => writeFileSync(path, JSON.stringify(value));
json(join(root, 'package.json'), { name: 'root', private: true });
json(join(consumer, 'package.json'), {
name: 'consumer', version: '1.0.0', dependencies: { target: '1.0.0' },
});
json(join(target, 'package.json'), { name: 'target', version: '1.0.0', main: 'index.js' });
writeFileSync(join(target, 'index.js'), "module.exports = 'correct-target';\n");
writeFileSync(join(root, 'pnpm-workspace.yaml'),
`packages:\n - packages/**\ninjectWorkspacePackages: ${injected}\noverrides:\n target: ${protocol}:./packages/target\n`);
const install = run(pnpm, ['install', '--offline', '--ignore-scripts',
'--no-frozen-lockfile', '--store-dir', join(base, 'store')], root);
console.log(`\n${protocol}:, injectWorkspacePackages=${injected}, install exit=${install.status}`);
if (install.status !== 0) {
console.log(install.stdout + install.stderr);
continue;
}
console.log('target symlink:', readlinkSync(join(consumer, 'node_modules/target')));
const resolution = run(process.execPath, ['-p', "require('target')"], consumer);
console.log(`require('target') exit=${resolution.status}`, resolution.stdout.trim());
if (resolution.status !== 0) console.log(resolution.stderr.split('\n').slice(0, 8).join('\n'));
}
}Describe the Bug
A root override such as target: workspace:./packages/target is not rebased for a nested workspace consumer. Tested on published pnpm 12.14.0, macOS 26.7.1 arm64.
| Override | injectWorkspacePackages | Result |
|---|---|---|
| workspace:./packages/target | false | Install exits 0 but creates a broken symlink; require('target') fails. |
| workspace:./packages/target | true | Install fails, looking for packages/deep/consumer/packages/target. |
| file:./packages/target | false | Install and require('target') succeed. |
| file:./packages/target | true | Install and require('target') succeed. |
The intended target is <root>/packages/target. Both packages are workspace members selected by packages/**.
With injection enabled, the error is:
Could not install from "<root>/packages/deep/consumer/packages/target" as it does not exist.
Failed to resolve target@workspace:./packages/target
With injection disabled, packages/deep/consumer/node_modules/target points to ../packages/target, which does not exist. The working file: control points to ../../../target. Disabling injection hides the install-time error but does not fix runtime resolution. Both file: controls also pass a subsequent offline --frozen-lockfile install.
Related but different: #14555 and #14710 concern file: override freshness checks; this report concerns relative workspace: overrides during a fresh install.
Reproduction prepared and executed with Codex.
Expected Behavior
The relative path in the root override should keep referring to /packages/target when applied to each importer, with the resulting link/path rebased for that consumer. It should work with and without injection. file: overrides already preserve the target in this reproduction.
Which Node.js version are you using?
v24.21.0; also reproduced with v26.2.0
Which operating systems have you used?
- macOS
- Windows
- Linux
If your OS is a Linux based, which one it is? (Include the version if relevant)
No response
Contributing a fix
- I am working on a fix
Activity
lazerg commentedon Oct 17, 2026
I opened #17016 for this. It anchors a
workspace:./pathoverride at the workspace root and rewrites it relative to each project, the same wayfile:andlink:overrides work. It fixes both pnpm 11 and pnpm 12, with and withoutinjectWorkspacePackages, and covers the whole issue.Written by an agent (Claude Code, claude-opus-5-5).
fix(overrides): measure a workspace: path in an override from the wor…
Update pnpm to v12.15.0 (#2046)
Update pnpm to v12.15.0 (#2046)