pnpm 12.0.0 was published on 2026-08-26 with the Rust CLI as the real CLI. The happy path is uneventful: same commands, same flags, same pnpm-lock.yaml format. The traps live in the edges, and every one of them shipped less than eight weeks ago, which means any assistant answering from training data will describe pnpm 11 behaviour with total confidence. These are the twelve things I check before pnpm 12 goes near a CI runner.
Short version: pin packageManager before you upgrade anything. pnpm 12 finally validates pnpm-workspace.yaml instead of dropping unknown keys in silence, but the check only fails hard when the project pins a pnpm version the running pnpm satisfies. With no pin, a typo in minimumReleaseAge stays a warning and your dependency cooldown stays off.
What actually breaks when you upgrade to pnpm 12?
- Pin the pnpm version before you touch anything else. The v12.0.0 release notes give the new error as
ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS, and the RC 9 notes spell out the condition: the command fails when the project pins a pnpm version that the running pnpm satisfies. Everywhere else, the same unknown key prints a warning, the install continues, and the setting is still dropped. Put the pin inpackage.json(or usedevEngines.packageManager) so the upgrade runs against a check with teeth:
{ "packageManager": "[email protected]" }
- Grep for a misspelled
minimumReleaseAgefirst. This is the one that made me write the list. Under pnpm 11 a mistyped key inpnpm-workspace.yamldisappeared without a word, andminimumReleaseAgeis the key that holds your supply-chain cooldown: the delay that stops an install from pulling a package published an hour ago. A typo there means every install has been taking the newest thing on the registry while the config file said otherwise, which is exactly the exposure window that npm addressed with its install-script allowlist. pnpm 12 suggests the closest real setting name when a key looks like a typo, so let it audit for you:
pnpm install --frozen-lockfile 2>&1 | grep -iE 'unrecognized|did you mean'
Run that on every repo before you bump a single dependency.
- Move the SSH rewrite out of the repo and into git config. Per pnpm's What's different in pnpm 12 post (2026-08-10), GitHub, GitLab and Bitbucket specifiers are identities now:
kevva/is-positive,github:…,git+https://…andgit+ssh://…all resolve to the same dependency, and pnpm never records an SSH URL for those hosts. Private repos that relied on the SSH form living in the lockfile fail to fetch on a runner with a deploy key. Push the rewrite down to git, where it applies to every fetch:
git config --global url."ssh://[email protected]/".insteadOf "https://github.com/"
Worth remembering what a git dependency is while you do this: upstream source you build locally, the same trust position that made the arrayref build.rs compromise work.
globalShimschanges whatnodemeans on a build agent. pnpm 12 addsglobalShims, defaulting to{ node: true, deno: true, bun: true }, so a globally installednodehonours the version the current project pins. That is excellent on a laptop and unwelcome on an agent that was built around one fixed toolchain and runs jobs from six repos. I setglobalShims: falseinpnpm-workspace.yamlon runners and keep version selection in the CI image, because a global binary whose meaning depends on the working directory turns "which node ran this build" into archaeology.pnpm add -g yarnnow installs current Yarn. The same post confirms pnpm 12 installs the actual package-manager tools rather than npm wrappers:pnpm add -g yarnlands on the current Yarn line, andpnpm add -g nodeinstalls a real Node.js release instead of a wrapper that downloads one at first run. Any bootstrap script that has been quietly shipping Yarn 1 for years now ships Yarn 4. Read your provisioning scripts before the runners do.--resolution-onlyis gone, and the failure carries no pnpm error code.pnpm peers checkreplaces it. What the old flag gets you is a clap argument-parser message:
error: unexpected argument '--resolution-only' found
Nothing in that line says ERR_PNPM_, which is why it gets filed as a shell quoting problem. Treat any bare error: unexpected argument from pnpm as "the Rust CLI dropped this flag" and go find the replacement command. It needs the same reading discipline as the npm approve-scripts and --allow-scripts split: the flag name that used to work tells you nothing about the one that works now.
- Audit
.pnpmfile.cjsforfilterLog. The v12.0.0 notes deprecate the pnpmfilefilterLoghook: the Rust CLI ignores it and emits a warning. Teams used it to scrub tokens and cut noise out of install logs, and that scrubbing stops the moment you upgrade, with a single warning line as the only marker. Check the file, then check what your CI log retention has been capturing since the upgrade landed. - Reject
..in workspace globs before pnpm does. Issue #13880, opened 2026-08-12, reports parent-relative workspace packages breaking on 12: pnpm omits them from discovery, then refuses the lockfile importer withRefusing to install importer with unsafe path key '../shared'. The message goes on to require POSIX paths relative to the workspace root, rejecting absolute paths, drive prefixes and..components. One grep tells you whether you are exposed:
grep -n '\.\.' pnpm-workspace.yaml
- Dry-run
engineStrictagainst your optional subtrees. UnderengineStrict, pnpm 12 fails when an incompatible package is reached through a regulardependenciesedge, even where that edge hangs below anoptionalDependenciessubtree. Packages tolerated for years because an optional parent covered for them now block the install outright. FlipengineStricton locally and run a full install before you change the version in CI. - Land the lockfile churn as its own commit. pnpm 12 breaks dependency cycles in a canonical order, which buys deterministic, smaller lockfiles and 2 to 3 times faster peer resolution, and it re-keys the affected peer variants on the first resolution pass after the upgrade. Run
pnpm install, commitpnpm-lock.yamlalone, and keep that diff away from any dependency bump. A reviewer can only spot a real change when the mechanical churn shipped separately. - Check
readlinkon the deploy output, because the build stays green either way. Issue #13618 documents a regression introduced in 11.19.0 and still labelled state: needs design:pnpm deploy --legacyleaves a workspace dependency that carriespeerDependenciesas a symlink escaping the deploy directory. CI passes, because the target still exists on the runner, and the container dies at startup withERR_MODULE_NOT_FOUND. One line in the pipeline catches it:
readlink deploy/node_modules/shared | grep -q '^\.\./\.\./' && exit 1
Workspace deps without peers still pack correctly, which is why the small reproduction someone builds to check this comes back clean.
- Drop the overrides you added for the ESLint
Intrinsiccrash. RC 10 (2026-08-24) removed statically-detected entries from the built-in compatibility database. Those entries had given@typescript-eslint/typesatypescriptdependency that resolved to the newest release, putting TypeScript 7 under older@typescript-eslintversions and making ESLint fail withCannot read properties of undefined (reading 'Intrinsic'). The database keeps its@yarnpkg/extensionsand pnpm-curated entries. Any pin you added to work around that crash is dead weight in the manifest, and dead pins outlive the people who remember why they exist.
Wrap-up
Pin the pnpm version before you upgrade, then read the warnings the pinned run produces. pnpm 12 has genuinely good diagnostics: typo suggestions, an unrecognized-settings error, a strict importer check. Almost all of them degrade to a warning, or to nothing at all, when the project gives them no pin to check against. That is the same failure mode as an npm audit that returns 410 and passes anyway: the tool tells you the truth only once you have configured it to be able to fail.
FAQ
Does pnpm 12 change the lockfile format? No. The format is unchanged from pnpm 11. The content changes, because cycles are now broken in a canonical order and the affected peer variants get re-keyed on the first resolution pass, so expect one large diff and commit it alone.
Why did my unknown pnpm-workspace.yaml key only warn? The project has no pnpm version pin. ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGS fails the command only when the project pins a version the running pnpm satisfies; otherwise you get a warning and the key is still ignored.
What replaced pnpm install --resolution-only? pnpm peers check. The removed flag surfaces as error: unexpected argument '--resolution-only' found, straight from the argument parser, with no ERR_PNPM_ code attached.
Is pnpm deploy safe on 12? pnpm deploy --legacy still carries issue #13618, open since 11.19.0 and labelled needs design. Add the readlink check from tip 11 to the pipeline rather than waiting for the fix.
What I read: the v12.0.0 release notes and RC 10 notes on GitHub, pnpm's own What's different in pnpm 12, and issues #13618 and #13880.
Comments
Be the first to comment.