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.

Unknown key in pnpm-workspace.yamlProject pins a pnpm version?ERR_PNPM_UNRECOGNIZED_WORKSPACE_SETTINGScommand failsWarning onlyinstall continues, key still ignored"yes, pnpm 12 satisfies it""no pin"

What actually breaks when you upgrade to pnpm 12?

  1. 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 in package.json (or use devEngines.packageManager) so the upgrade runs against a check with teeth:
   { "packageManager": "[email protected]" }
  1. Grep for a misspelled minimumReleaseAge first. This is the one that made me write the list. Under pnpm 11 a mistyped key in pnpm-workspace.yaml disappeared without a word, and minimumReleaseAge is 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.

  1. 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://… and git+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.

  1. globalShims changes what node means on a build agent. pnpm 12 adds globalShims, defaulting to { node: true, deno: true, bun: true }, so a globally installed node honours 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 set globalShims: false in pnpm-workspace.yaml on 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.
  2. pnpm add -g yarn now installs current Yarn. The same post confirms pnpm 12 installs the actual package-manager tools rather than npm wrappers: pnpm add -g yarn lands on the current Yarn line, and pnpm add -g node installs 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.
  3. --resolution-only is gone, and the failure carries no pnpm error code. pnpm peers check replaces 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.

  1. Audit .pnpmfile.cjs for filterLog. The v12.0.0 notes deprecate the pnpmfile filterLog hook: 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.
  2. 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 with Refusing 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
  1. Dry-run engineStrict against your optional subtrees. Under engineStrict, pnpm 12 fails when an incompatible package is reached through a regular dependencies edge, even where that edge hangs below an optionalDependencies subtree. Packages tolerated for years because an optional parent covered for them now block the install outright. Flip engineStrict on locally and run a full install before you change the version in CI.
  2. 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, commit pnpm-lock.yaml alone, and keep that diff away from any dependency bump. A reviewer can only spot a real change when the mechanical churn shipped separately.
  3. Check readlink on 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 --legacy leaves a workspace dependency that carries peerDependencies as a symlink escaping the deploy directory. CI passes, because the target still exists on the runner, and the container dies at startup with ERR_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.

  1. Drop the overrides you added for the ESLint Intrinsic crash. RC 10 (2026-08-24) removed statically-detected entries from the built-in compatibility database. Those entries had given @typescript-eslint/types a typescript dependency that resolved to the newest release, putting TypeScript 7 under older @typescript-eslint versions and making ESLint fail with Cannot read properties of undefined (reading 'Intrinsic'). The database keeps its @yarnpkg/extensions and 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.