D
P
0
← All articles Baca dalam Bahasa Indonesia

Next.js & React in Production

`ERR_PNPM_IGNORED_BUILDS` Stopped Deploys With No Commit Behind It: pnpm 11 Only Reads `allowBuilds`

· · 6 min read
`ERR_PNPM_IGNORED_BUILDS` Stopped Deploys With No Commit Behind It: pnpm 11 Only Reads `allowBuilds`

Pushes land, git is clean, every ref matches, and production quietly keeps serving the old bundle. That is what it looked like on 29 August 2026, when two pushes to the client's production branch produced no deploy. Once I traced it, production turned out not to have deployed since 21 August, eight days. The client's CI build on AWS CodeBuild failed at pnpm install, before a single line was compiled.

The log is short and leaves little room for interpretation:

Error: ERR_PNPM_IGNORED_BUILDS
  Ignored build scripts: esbuild@0.27.7, sharp@0.34.5, unrs-resolver@1.11.1
  Command did not exit successfully pnpm install exit status 1

I have written before about a Turbopack build that failed while the old version kept serving, and at first glance this case looks like the same family. The difference is where the problem came from. There, the cause lived in the repo: a package that was imported without being declared. Here, nothing in the repo caused it.

No commit behind it

The source was pnpm. According to the changes pnpm itself shipped, version 10 stopped running dependency lifecycle scripts unless they had been decided, and a later release promoted the leftover warning to a hard error. The number of that later release is not in my notes.

An upstream change like that normally reaches a project when the project chooses to upgrade. This project upgraded without anyone choosing. At the time package.json had no packageManager field, which left the build image to install whichever pnpm happened to be newest. The day that version shipped, the build broke on its own, with no commit behind it.

That is the real lesson here. As long as the package manager is unpinned, the build is a moving target, and it will break on exactly the day you changed nothing.

Three spellings, only one of them read

Fixing it was not as simple as naming which packages may run build scripts. That decision can be written in three places, and on pnpm 11 only one of them is read:

package.json, "pnpm" field
  onlyBuiltDependencies                              not read on pnpm 11: older location, silently ignored
pnpm-workspace.yaml
  onlyBuiltDependencies / ignoredBuiltDependencies   not read on pnpm 11: older list form
pnpm-workspace.yaml
  allowBuilds (map of package name to boolean)       read

Two clean-install cycles went to the first two spellings before pnpm itself rewrote pnpm-workspace.yaml and scaffolded allowBuilds: with set this to true or false placeholders. Since then I keep one rule: when unsure which key a pnpm version wants, run the install and let pnpm write the file, because it names the key it reads.

The file already held a decision

There was another mistake, more dangerous than a wrong spelling. pnpm-workspace.yaml already existed and held a deliberate, earlier decision. Its git history shows that content arriving in a commit titled "Initial commit from Create Next App", dated 20 April 2026:

ignoredBuiltDependencies:
  - sharp
  - unrs-resolver

I overwrote that file without reading it first, which flipped sharp and unrs-resolver to true. My estimate is that the flip would only have swapped one failure for another, since CodeBuild runs on Linux and sharp's build script wants libvips, while it passed on my Mac. That is a guess, not a test result, because those values never shipped.

The mistake was caught only because git status marked the file M rather than showing it as a new file. Without that one letter I would not have noticed. The rule since then: read a config file before writing it, and treat M in the diffstat as a signal that you are changing someone else's decision.

What shipped: everything false

What finally shipped preserves that original intent instead of reversing it. The reasoning is that all three packages ship prebuilt platform packages, namely @img/sharp-*, @esbuild/* and @unrs/*, which the install script only links. The block that went into pnpm-workspace.yaml:

allowBuilds:
  esbuild: false
  sharp: false
  unrs-resolver: false

The error named three packages, but they were not in the same position. The esbuild package had never been decided either way. The sharp and unrs-resolver packages had been decided, only in the older list form. The comment I left in that file spells out the chain: current pnpm reads allowBuilds, the older list form is not read, so the previous contents of the file stopped counting as a decision and all three packages were reported as undecided.

The same comment carries a warning. Flipping one to true "means letting it execute code at install time on the build box, so make it a real decision and re-verify, not a reflex when something looks missing."

The fix went in as a commit titled fix(build): state the install-script decision in the key current pnpm reads, then reached main through a merge on 29 August that changed a single file, pnpm-workspace.yaml, with 30 insertions and 3 deletions.

The install exit code I skipped

One more mistake had nothing to do with any key. Earlier the same day I had reported "265 tests pass" while pnpm install had itself exited 1. The node_modules left behind was still populated enough for tsc and vitest to run. Those test results were real, but they said nothing about the install that had just failed. Missing that one exit code cost five hours.

The verification that actually counted started from nothing: delete node_modules, then install, tsc, tests and a full next build, all exiting 0. The fix's commit message records the same figure, 265 tests, this time from a deleted node_modules. The test count did not change. Only the install exit code in front of it did.

# simplified: read the install exit code first, nothing after it counts until this is 0
rm -rf node_modules
pnpm install
echo "install exit: $?"

The rule: check the install's exit code, not just what runs afterwards.

What is proven and what is not

One thing I need to keep separate. The verification above was a clean install, not a CodeBuild log, and my notes contain no sentence saying the CodeBuild build went green again after this fix. The nearest trace is indirect. According to my notes, the live commit was proven through route existence: an API route answering 200 placed one merge dated 31 August on production, and a POST route answering 204 placed another merge from the same day. Both of those merges already carry the allowBuilds block with its three false values.

As for the root cause, the unpinned package manager, the packageManager line was only added to package.json on 31 August, after those two merges, in a commit titled build: pin pnpm to 11.21.0 so the build stops being a moving target. The pin has a story of its own, and that one belongs in a separate article.

A build that breaks on a day nothing changed

This case left four rules behind, and only one of them is specific to pnpm: