📦

pnpm v10 へのアップグレードで Vercel ビルドが失敗した話

に公開

こんにちは、steshima です。

ソーシャル PLUS のフロントエンドでは、パッケージマネージャーに pnpm を使用しています。
先日、その pnpm のバージョンを v9 から v10 にアップグレードしたところ、Vercel 上でのビルドが失敗するようになりました。

原因や対応内容を備忘録として残しておきます。

発生したエラー

ビルド失敗時、Vercel では下記のビルドログが出力されていました。

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 corepack (https://vercel.com/docs/deployments/configure-a-build#corepack)
Running "install" command: `pnpm install`...

...

ERR_PNPM_LOCKFILE_CONFIG_MISMATCH  Cannot proceed with the frozen installation. The current "patchedDependencies" configuration doesn't match the value found in the lockfile

Update your lockfile using "pnpm install --no-frozen-lockfile"
Error: Command "pnpm install" exited with 1

ログを読むと、2 つのことが読み取れます。

  1. Vercel が pnpm v9 を使用している
  2. patchedDependencies の設定がロックファイルと一致していない

まず Vercel が v9 を使っているのが問題だろうと当たりをつけ、そちらから対処することにしました。

Vercel の pnpm バージョン選択の仕組み

lockfileVersion はスキーマのバージョンであり pnpm のバージョンではない

ビルドログの中に Detected pnpm-lock.yaml 9 とあります。これは pnpm-lock.yaml の先頭に記載されている lockfileVersion の値を指しています。

lockfileVersion: '9.0'

この 9.0pnpm のバージョンではなく、ロックファイルのスキーマバージョンです。pnpm v9 でも v10 でも同じ 9.0 が使われるため、ログにも which may be generated by pnpm@9.x or pnpm@10.x と書かれているとおり、どちらのバージョンで生成されたか判別できません。

そのため Vercel はスキーマバージョンだけでは判断できず、その場合プロジェクトの作成日を基準に pnpm のバージョンを決定する動作をとることがビルドログから読み取れます。

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

該当プロジェクトは pnpm v9 の時代に作成されていたため、v9 が選ばれていたと考えられます。

Vercel で pnpm のバージョンを明示的に指定する方法

Vercel でパッケージマネージャーのバージョンを明示的に指定する方法は公式ドキュメントにまとめられています。

https://vercel.com/kb/guide/how-do-i-use-the-latest-npm-version-for-my-vercel-deployment

大きく 2 つの方法があります。

方法① Install Command を上書きする

Vercel のプロジェクト設定から Build & Development Settings > Install Command を上書きすることで、使用する pnpm のバージョンを指定できます。

シンプルに特定バージョンを使いたい場合はこちらが手軽です。

方法② Corepack を有効化する

Corepack を有効化すると、package.jsonpackageManager フィールドを参照してパッケージマネージャーのバージョンを決定してくれます。

{
  "packageManager": "pnpm@10.33.0"
}

Vercel プロジェクトの環境変数に以下を追加するだけで有効化できます。

ENABLE_EXPERIMENTAL_COREPACK=1

今回はモノレポ環境下での packageManager フィールドによるバージョン管理を一元化したかったため、方法②の Corepack を選択しました。

対処① Corepack の適用環境を修正する

少し補足すると、ソーシャル PLUS では Vercel のデフォルトの環境(Development・Preview・Production)に加え、独自に staging 環境を追加しています。この staging 環境はステージング用ドメインを設定しており、master ブランチをトラッキングすることで、PR がマージされると自動的にステージング環境が更新されるような運用にしています。
PR 作成時は Vercel が自動で Preview 環境のデプロイを行います。

そして、元々 ENABLE_EXPERIMENTAL_COREPACK=1 の環境変数は v10 へのアップグレード時に設定済みで、アップグレード PR ではビルドログで v10 が使われていることも確認済みでした。
しかしよく確認すると設定にミスがあり、 staging 環境には適用されていなかったため、master マージ後にデプロイされる staging 環境では有効になっていませんでした。

PR 作成時のプレビューデプロイでは pnpm v10 が正しく使われていたため、この設定漏れに気づけていませんでした。

そのため環境変数の適用環境に staging も追加し再デプロイしたところ、ビルドログから Using pnpm@9.x の行が消え、pnpm v10 が使われるようになりました。

しかし、それでも同じ ERR_PNPM_LOCKFILE_CONFIG_MISMATCH エラーは引き続き発生していました。

Vercel が pnpm v9 を使っていたことは問題の一つではありましたが、それだけがエラーの原因ではなかったようです。

対処② ロックファイルを再生成する

次にロックファイルを削除し、 pnpm install で再生成しました。

rm pnpm-lock.yaml
pnpm install

すると、すんなり Vercel のビルドが通るようになりました 😓

結局 patchedDependencies の不一致がなぜ起きていたのか、正確な原因は特定できませんでした。

ただ、PR 作成時のプレビュー環境ではこのエラーが発生していなかったことを踏まえると、pnpm v10 アップグレードの PR と同じくらいのタイミングに別のパッケージ更新 PR をマージしていたので、そちらが影響していた可能性がありそうです。
master へマージしたタイミングでロックファイルが不整合な状態になり、問題が顕在化したのかもしれません。

いずれにせよ、ロックファイルを再生成することでエラーは解消されました。

まとめ

  • pnpm-lock.yamllockfileVersion はスキーマバージョンであり pnpm のバージョンではない。Vercel はこれを判別できないため、デフォルトではプロジェクト作成日を基準に pnpm バージョンを決定する
  • Vercel で pnpm のバージョンを明示的に指定するには、Install Command の上書きか Corepack の有効化の 2 つの方法がある
  • patchedDependencies の不一致が発生した場合、ロックファイルをゼロから再生成すると解決することがある
Social PLUS Tech Blog

Discussion