Rebounder Tech Blog

Written by the people who actually run these systems in production.

pnpm-lock.yaml Made Vercel Misdetect an npm Project as pnpm

Published About 3 min readBy the Rebounder engineering team — the people who operate these systems

This article may contain affiliate links. Its content is not affected by advertising.

In short

Once a `.vercelignore` file exists, the Vercel CLI stops consulting `.gitignore`, so an untracked `pnpm-lock.yaml` that was never committed still gets uploaded, and Vercel detects the project as pnpm.

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.

Frequently asked questions

Q1What changes once .vercelignore exists?

The Vercel CLI consults only .vercelignore for what to exclude from the upload once that file exists, and it stops reading .gitignore. This repo's .vercelignore never listed pnpm-lock.yaml or pnpm-workspace.yaml, so those files were uploaded even though they had never been committed to git.

Q2How did npm and pnpm resolve the dependency differently?

package.json pinned stripe to ^22.3.1, and the npm-side package-lock.json resolved it to exactly 22.3.1. The pnpm-lock.yaml left over locally had resolved it to 22.6.0 instead, and once Vercel detected pnpm it used that lockfile, so the mismatched type definitions broke the build with a type error.

Q3Was the fix to delete pnpm-lock.yaml?

No, the file was not deleted. The fix was adding pnpm-lock.yaml and pnpm-workspace.yaml explicitly to .vercelignore so they are excluded from the Vercel upload. The local files themselves are still there.

Environment verified

  • package.json pins stripe to ^22.3.1 (npm resolves 22.3.1 / pnpm resolves 22.6.0)
  • Found and fixed on 2026-09-16 during a pre-deploy review of kimiteras-portal

What this article is based on

  • file lines 1-14commit 3e95468
  • JSON file lines 26-26commit 62a03f7
  • JSON file lines 7979-7981commit 62a03f7

Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.