Conclusion
The canonical package manager for this repo is npm (package-lock.json), but a pnpm-lock.yaml that had never once been committed to git was still sitting in the working directory, and it made Vercel misdetect the project as pnpm, breaking the build with a type error. Once a .vercelignore file exists, the Vercel CLI stops reading .gitignore, so anything not explicitly listed in .vercelignore gets uploaded — committed or not.
Symptom
Dependency management in this repo is npm, and package.json pins stripe to ^22.3.1.
"stripe": "^22.3.1",
npm resolves that to 22.3.1 and writes it into package-lock.json.
"node_modules/stripe": {
"version": "22.3.1",
"resolved": "https://registry.npmjs.org/stripe/-/stripe-22.3.1.tgz",
But the working directory also still had pnpm-lock.yaml and pnpm-workspace.yaml sitting in it, both of which had never once been git added. Pushing the build to Vercel failed with a type error caused by a mismatch in stripe's type definitions.
Cause
.vercelignore originally read like this.
.env*
/scripts
/supabase
/test-results
/README.md
pnpm-lock.yaml is not in that list. Once a .vercelignore file exists, the Vercel CLI decides what to exclude from the upload using only what's written in .vercelignore, and it does not consult .gitignore at all. So the usual assumption — "it's fine, I never pushed it to git" — does not hold here: the untracked pnpm-lock.yaml / pnpm-workspace.yaml files sitting in the working directory were uploaded as-is.
Vercel's build environment detects a project as pnpm based on the presence of pnpm-lock.yaml among the uploaded files. Once that detection lands on pnpm, dependency resolution follows the pnpm lockfile too. In pnpm-lock.yaml, stripe had resolved to
stripe@22.6.0:
which differs from the 22.3.1 that the npm side had pinned. That gap surfaced directly as the type error that broke the build.
The fix
Two lines were added to .vercelignore, explicitly excluding pnpm-lock.yaml and pnpm-workspace.yaml from the upload.
/pnpm-lock.yaml
/pnpm-workspace.yaml
The local files themselves were not deleted. They simply no longer get uploaded to Vercel — .vercelignore remains the single place that decides what gets uploaded, so any untracked file you want excluded still has to be listed there going forward.
Preventing a repeat
This article's sources don't include any record of a mechanical check being added to detect a stray pnpm lockfile. The fix here was also just adding the two files that were found to .vercelignore.
What this exposes structurally is that "not pushed to git" and "not uploaded to Vercel" are two different claims. The moment a .vercelignore file is created at all, the basis for exclusion switches from .gitignore to whatever .vercelignore itself says. An untracked file sitting around locally causes no harm on its own — but in a repo that has a .vercelignore, whether that file is the kind that conflicts with the canonical package manager needs to be checked the moment it appears, not later.
Originally published at tech.rebounder.jp.
Top comments (0)