{} CodeLift

Laravel の Vite manifest not found を原因別に切り分ける

デプロイ直後に Vite manifest not found が出たとき、検索して見つかる答えはほぼ npm run build の一択である。だが Docker で 6 ケースを実測したところ、ビルドで直るのは 4 つある原因のうち 1 つだけだった。

公開 2026-08-02 検証 2026-08-02 更新 2026-08-12

検証環境

  • Laravel 13.23.0
  • PHP 8.4.24
  • Node 22.23.2
  • Cases reproduced 6 — two of them return HTTP 200
  • Key finding npm run build does not remove public/hot
  • OS Docker Desktop (php:8.4-cli-bookworm)

デプロイ直後に Vite manifest not found が出たとき、検索して見つかる答えはほぼ npm run build の一択である。だが Docker で 6 ケースを実測したところ、ビルドで直るのは 4 つある原因のうち 1 つだけだった。

npm run build を実行してください。

さらに厄介なのは、4 つの原因のうち 2 つはエラーを一切出さないことである。HTTP 200 が返り、ログにも何も残らず、画面だけが崩れる。

ビルドは実際に走らせている。manifest.json を手書きしてそれらしく見せてはいない。

スクリプトは laravel-error-lab に置いてある。

検証環境

Laravel 13.23.0
PHP 8.4.24
Node 22.23.2
実行環境 Docker Desktop / php:8.4-cli-bookworm

正常系から一度に 1 つだけ、ディスク上の状態を変えて計測している。

6 ケースの実測結果

ケース 変更点 HTTP 例外
A (正常系) 200
B public/build ごと削除 500 ViteManifestNotFoundException
C public/hot が残っている(build はある) 200 なし
D public/hot が残っていて build も無い 200 なし
E manifest が public/build/.vite/ にだけある 500 ViteManifestNotFoundException
F @vite() が manifest に無いエントリを参照 500 ViteException

npm run build が答えになるのは B だけである。

例外は 2 種類ある — 混同されている

「manifest 関連のエラー」とひとくくりにされがちだが、投げられるクラスもメッセージも違う。

B / E:

Illuminate\Foundation\ViteManifestNotFoundException
Vite manifest not found at: /app/public/build/manifest.json

F:

Illuminate\Foundation\ViteException
Unable to locate file in Vite manifest: resources/js/does-not-exist.js.

F は「manifest は読めた。だがその中に指定されたエントリが無い」という意味で、原因も対処もまったく別である。@vite() に書いたパスと vite.config.jsinput が食い違っているときに出る。ビルドし直しても直らない。

エラーメッセージを最後まで読めば区別できるが、どちらも「Vite manifest」で始まるので、検索するとき同じ言葉になってしまう。

B と E は見分けがつかない

E は manifest ファイル自体は存在しているpublic/build/.vite/manifest.json にある。それでもエラーは B と一字一句同じで、public/build/manifest.json が無いと言われる。

Laravel が見る場所は固定である。

Illuminate/Foundation/Vite.php

 56:  protected $manifestFilename = 'manifest.json';
975:  protected function manifestPath($buildDirectory)
977:      return public_path($buildDirectory.'/'.$this->manifestFilename);

laravel-vite-plugin を使っていれば manifest は public/build/manifest.json に置かれる。素の Vite(Vite 5 以降)は .vite/manifest.json に出力するので、プラグインを外した / 独自の Vite 設定を組んだ場合にこの状態になる

「ファイルはあるのに無いと言われる」ので、パスを実際に確認するまで気付けない。

ls -la public/build/manifest.json public/build/.vite/manifest.json

最も厄介なのは C — エラーが出ない

ここが本題である。

public/hot というファイルが残っていると、HTTP 200 が返り、例外は一切発生しない。ページは正常にレンダリングされる。

そのページが読み込もうとしているものはこれである。

<script type="module" src="http://127.0.0.1:5173/@vite/client"></script>
<link rel="stylesheet" href="http://127.0.0.1:5173/resources/css/app.css" />
<script type="module" src="http://127.0.0.1:5173/resources/js/app.js"></script>

127.0.0.1:5173 は Vite の開発サーバである。本番にそんなものは動いていない。ブラウザから取りに行くと:

http://127.0.0.1:5173/@vite/client -> connection refused (nothing is listening)

サーバのログには何も残らない。ステータスは 200。監視も素通りする。 見えるのは「なぜか CSS が当たっていないページ」だけである。

なぜこうなるのか

Illuminate/Foundation/Vite.php

 239:  return $this->hotFile ?? public_path('/hot');
1223:  return is_file($this->hotFile());

isRunningHot()public/hot というファイルが存在するかどうかだけを見る。中身も、開発サーバが実際に生きているかも確認しない。

true が返れば、manifest は一切読まれない。だから D(hot ファイルあり + build 無し)でも 200 が返る。ビルド成果物の有無は関係なくなる。

public/hotnpm run dev が作る。正常に終了すれば消えるが、Ctrl+C の効かない止め方をしたり、プロセスが強制終了されたりすると残る。それがそのまま git add されてデプロイに乗ることもある。

npm run build はこれを直せない

定番の助言が効かないことを実測した。

after 'npm run build' with a hot file present: STILL THERE

ビルドは public/hot を削除しない。 何度ビルドし直しても状況は変わらない。必要なのはこれである。

rm public/hot

public/hot.gitignore に入れておくべきファイルでもある。Laravel の標準 .gitignore には含まれているが、独自に書き換えている場合は確認する価値がある

切り分け手順

ディスクを 3 回見るだけで、どのケースかが決まる。

# 1. hot ファイルが残っていないか(残っていたらこれが原因。エラーは出ない)
ls -la public/hot

# 2. manifest がどこにあるか
ls -la public/build/manifest.json public/build/.vite/manifest.json

# 3. manifest の中身と @vite() の指定が一致しているか
cat public/build/manifest.json | head -20
見えたもの ケース 対処
public/hot が存在する C / D rm public/hot
public/build/manifest.json が無い B npm run build
.vite/manifest.json にだけある E laravel-vite-plugin を使うか、出力先を合わせる
manifest はあるがエントリが無い F @vite() のパスと vite.config.jsinput を一致させる

エラーが出ていないのに CSS が効かない場合は、まず 1 を見る。 500 が出ている場合はメッセージのクラス名まで読む — ViteManifestNotFoundException なら 2、ViteException なら 3 である。

デプロイで再発させないために

C は「開発中のファイルが本番に紛れ込む」タイプの事故なので、デプロイ側で潰せる。

# デプロイスクリプトの、ビルドの前後どちらかに
rm -f public/hot

1 行で、C と D の両方が構造的に起きなくなる。

よくある質問

Q. npm run build を実行しても直りません

public/hot が残っている可能性が高いです。ビルドはこのファイルを削除しません(実測済み)。ls -la public/hot で確認し、あれば rm public/hot してください。

このケースはそもそも Vite manifest not found が出ません。HTTP 200 が返り、CSS と JS だけが読み込まれない状態になります。

Q. public/hot とは何ですか

npm run dev が作るファイルで、中身は Vite 開発サーバの URL です。Laravel はこのファイルが存在するかどうかだけを見て、あれば manifest を読まずに開発サーバへ向けます(Vite.php:1223)。開発サーバが実際に動いているかは確認しません。

npm run dev が正常終了すれば消えますが、強制終了すると残ります。

Q. manifest.json はあるのに「見つからない」と言われます

Laravel が見るのは public/build/manifest.json の 1 か所だけです(Vite.php:977)。素の Vite 5 以降は .vite/manifest.json に出力するため、laravel-vite-plugin を使っていない場合に食い違います。

ls -la public/build/.vite/manifest.json で確認してください。エラーメッセージは「ファイルが無い」場合と完全に同一なので、パスを見るまで区別できません。

Q. Unable to locate file in Vite manifest は同じ問題ですか

違います。こちらは ViteException で、manifest の読み込み自体は成功しています@vite() に書いたパスが manifest の中に無いという意味です。

@vite(['resources/js/app.js']) のパスと vite.config.jsinput が一致しているかを確認してください。ビルドし直しても直りません。

Q. 本番で急にスタイルが崩れましたが、エラーは何も出ていません

public/hot を疑ってください。このファイルがあると HTTP 200 が返り、例外も出ず、サーバのログにも何も残りません。ブラウザの開発者ツールで、アセットの取得先が localhost:5173 になっていないか確認するのが早い判別法です。

再現

git clone https://github.com/codelift-dev/laravel-error-lab
cd laravel-error-lab
docker compose build
docker compose run --rm lab bash vite-manifest.sh          # 6 ケースの再現
docker compose run --rm lab bash vite-manifest-detail.sh   # 例外の正確な文言

Docker 内で完結する。ホストに PHP も Node も要らない。

Laravel 13.23.0 / Node 22.23.2 で確認している。Vite.php の行番号は将来ずれる可能性があるが、isRunningHot() が hot ファイルの存在だけを見る構造と、manifest のパスが public/build/manifest.json 固定である点は Laravel 9 以降変わっていない。

関連記事