Fixing pnpm's packages field error on Vercel
A settings-only pnpm-workspace.yaml deployed fine locally but broke Vercel with "packages field missing or empty". The real cause was pnpm 9 versus 10, corepack, and a dead serverless region left over from an old project.
Deploying this portfolio to Vercel should have been a formality. The build passed locally, the lockfile was committed, and the project had lived on Vercel for years. Instead the very first deploy died during install with an error that made no sense for a single-package repo. Here is the whole chain of failures, because the fix was three layers deep and the useful clue was hiding above the error, not in it.
What the repo looked like
My machine runs pnpm 11.9.0. The repo carries a pnpm-lock.yaml with lockfileVersion '9.0' and a pnpm-workspace.yaml that holds only settings, no packages: field:
# pnpm-workspace.yaml
allowBuilds:
sharp: true
unrs-resolver: true
ignoredBuiltDependencies:
- sharp
- unrs-resolverThat is normal in pnpm 10 and later, where pnpm-workspace.yaml doubles as the settings file even for a single-package project. There is no workspace here, just a place to park build settings. The Vercel project itself was old. It used to host the previous version of the site, which turns out to matter a lot.
The failure
The first vercel deploy failed during install with a flat contradiction of what I was looking at:
ERROR packages field missing or empty
Error: Command "pnpm install" exited with 1The packages field was missing on purpose. Locally that is valid. On Vercel it was fatal. Something on the remote was reading my settings file as a workspace manifest and demanding a field that has no reason to exist.
The part that was easy to miss
The explanation was in the build log, printed several lines before the error where it is easy to scroll past:
Detected pnpm-lock.yaml 9 which may be generated by pnpm@9.x or pnpm@10.x
Using pnpm@9.x based on project creation date
To use pnpm@10.x, manually opt in using corepackThat is the whole story in three lines. Both pnpm 9 and pnpm 10 or 11 write lockfileVersion 9, so Vercel cannot tell them apart from the lockfile alone. When it cannot tell, it guesses from the date the project was created. My project is old, so it got pnpm 9. And pnpm 9 treats any pnpm-workspace.yaml as a workspace manifest that must declare packages:. A settings-only file, perfectly legal under pnpm 10, is a hard error under pnpm 9. The mismatch was never about my code. It was about which pnpm ran on the server.
The fix that did nothing
The obvious move was to pin the package manager. I added the field to package.json:
"packageManager": "pnpm@11.9.0"I redeployed and got the exact same failure with identical logs. Vercel ignored the field entirely. The packageManager field is a corepack instruction, and corepack was not enabled on this project, so the field was inert. Declaring the right version means nothing if nothing is listening for it.
The fix that worked
Corepack on Vercel is gated behind an environment variable, ENABLE_EXPERIMENTAL_COREPACK=1. I tested it per deployment first to confirm the theory:
vercel deploy -b ENABLE_EXPERIMENTAL_COREPACK=1Install and build passed immediately. With corepack on, the packageManager field finally did its job and told corepack to run pnpm 11, which reads the settings-only workspace file without complaint. Two settings that only work together: the field names the version, the variable lets corepack act on it.
Plot twist one, a dead region
The install problem was solved, so of course a new one appeared. The deploy then failed at the release step:
Invalid region: dxb1The old project had a serverless region stored in its settings, dxb1, that is no longer accepted at deploy time. Nothing in my code referenced it. It was stale project state from years ago. The fix was to pin the region explicitly in vercel.json, which overrides the stored setting:
{
"regions": ["cdg1"]
}Plot twist two, promote rebuilds
With a working preview deployment in hand, I tried to promote it to production. That failed too. The reason is that vercel promote does not just repoint traffic at an existing build. It rebuilds the deployment for production, and the corepack variable had only ever been passed per deployment through the -b flag. The rebuild had no idea corepack should be on, so it fell straight back to pnpm 9 and the original error.
The durable fix was to stop passing the variable as a one-off and persist it as a project environment variable for both Production and Preview:
vercel env add ENABLE_EXPERIMENTAL_COREPACKAfter that vercel deploy --prod worked, and the Git-triggered auto-deploys have kept working since, because every build now sees the variable regardless of how it was started.
Takeaways
lockfileVersion 9is ambiguous across pnpm 9, 10 and 11. Declare your package manager explicitly with thepackageManagerfield so nothing has to guess.- On Vercel that field does nothing unless corepack is enabled with
ENABLE_EXPERIMENTAL_COREPACK=1, persisted as a project environment variable rather than a one-off deploy flag. - Old projects carry stale settings, like a serverless region that no longer exists.
vercel.jsonis the way to pin over whatever the project has stored. - Read the lines above the error in build logs. The reason Vercel chose pnpm 9 was printed before the failure, not with it.
None of these were bugs in the site. They were three pieces of remote state, pnpm version selection, an unenabled feature flag and a dead region, all inherited from a project that predated the code I was shipping. The lesson I keep relearning is that a green build on my machine says nothing about the assumptions baked into the server it lands on.