Rebounder Tech Blog

運用している当事者が書く、本番システムの記録。

pnpm-lock.yamlが未追跡でもVercelはpnpmと誤認識する

公開 読了時間 約3分執筆: Rebounder 開発チーム(当該システムの運用当事者)

※本記事にはアフィリエイトリンクを含む場合があります。内容は広告の有無に影響されません。

結論

.vercelignoreは.gitignoreとは独立した除外リストで、git管理外のpnpm-lock.yamlであっても明示的に除外しない限りそのままアップロードされ、Vercelはプロジェクトをpnpmと誤認識する。

結論

.vercelignoreは.gitignoreとは独立した除外リストで、git管理外のpnpm-lock.yamlであっても明示的に除外しない限りそのままアップロードされ、Vercelはプロジェクトをpnpmと誤認識する。

症状

このリポジトリの正本はnpmで、package-lock.jsonをgitで追跡している。ところがローカルの作業ツリーには、過去に何かのきっかけで作られた未追跡のpnpm-lock.yamlとpnpm-workspace.yamlが残っていた。.gitignoreには入れていなかったので、git statusでは毎回「追跡されていないファイル」として表示され続けていたはずだが、ビルドが通る限りは実害が無いため見過ごされていた。

次のデプロイに備えたレビューで.vercelignoreを確認したところ、この2ファイルを除外する記述が無いことに気づいた。このツリーは7/31以降一度もVercelへデプロイしておらず、実際にアップロードした場合にどう扱われるかは未検証のまま残っていた。

原因

Vercel CLIが本番ビルドへアップロードするファイルの範囲は、.vercelignoreだけで決まる。.gitignoreは見ない。したがって、gitの追跡対象に入れていないファイルであっても、.vercelignoreで明示的に除外しない限りそのままアップロードされる。

アップロードされたファイルの中にpnpm-lock.yamlがあると、Vercelはこのプロジェクトのpackage managerをpnpmとしてselectする。pnpmはpackage-lock.jsonの内容を見ず、package.jsonの依存範囲指定を自分の解決アルゴリズムで解決し直す。このプロジェクトのpackage.jsonはstripeを"^22.3.1"という範囲で指定していたが、npm側の解決結果は22.3.1、pnpm側の解決結果は22.6.0で異なっていた。pnpmが選ばれてこの版がインストールされると、stripeの型定義がコード側の前提とずれ、型チェックでビルドが落ちる経路になる。

同じ範囲指定なのに解決結果が割れるのは、npmとpnpmが別のタイミングで依存解決を行うため、参照するレジストリの状態が食い違うことがあるからだ。この食い違い自体は珍しくないが、**ふだんはpnpmが選ばれることが無いので表面化しない。**今回のように.vercelignoreの記述漏れでpackage managerの判定そのものが切り替わった場合にだけ、ズレが型エラーとして現れる。

直す

.vercelignoreに/pnpm-lock.yamlと/pnpm-workspace.yamlを追記し、そもそもアップロードさせないようにした。これでVercelが参照できるロックファイルはpackage-lock.jsonだけになり、package managerの判定はnpmに固定される。

/pnpm-lock.yaml
/pnpm-workspace.yaml

再発防止

この問題が見えにくかった理由は、ローカルのnext buildではVercelのアップロード対象を再現できないことにある。ローカルではpackage managerをCLIが明示的に実行するので、未追跡のpnpm-lock.yamlが存在していてもnpm run buildの結果には何の影響も出ない。影響が出るのはVercel側がアップロードされたファイル一覧からpackage managerを判定する瞬間だけで、しかもそれは実際にデプロイするまで検証できない。

.vercelignoreには、なぜこの2行が必要かを示すコメントをそのまま残した。除外リストは機械的な設定ファイルなので、理由を書いておかない限り将来誰かが「使っていないから」と削ってしまう。この種の設定は、結果だけを書いても再発防止にならない。

よくある質問

Q1.gitignoreに書けば防げますか?

.vercelignoreは.gitignoreを見ないため防げない。pnpm-lock.yamlとpnpm-workspace.yamlを.vercelignore自体に明記して除外する必要がある。

Q2pnpm-lock.yamlを削除すれば直りますか?

削除しても直るが、再度pnpm installを実行すれば同じファイルが未追跡のまま作られ直す。.vercelignoreで除外しておけば、ファイルがローカルに残っていてもアップロードされない。

Q3package-lock.jsonとpnpm-lock.yamlでstripeのバージョンが違ったのはなぜですか?

package.jsonの"^22.3.1"という範囲指定に対し、npmとpnpmは別のタイミングで依存解決した結果、同じ範囲指定でも22.3.1と22.6.0に分かれていた。

確認した環境

  • Next.js 16.2.7(package.json)
  • 2026-09-16 にデプロイ前レビューで発覚・同日修正

この記事の根拠

  • ファイル 1〜14行目コミット 3e95468
  • JSONファイル 18〜29行目コミット 3e95468

本文の主張は、上の記録に書かれていることだけです。運用しているリポジトリは非公開のため リンクは張れませんが、どのファイルの何行目を、どのコミット時点で見て書いたかは 記事ごとに残しています。推測で書いた箇所はありません。