The problem: After deploying or running npm run build, Laravel throws a Vite manifest exception and every page using @vite() in Blade breaks with a 500 error.
Environment: Laravel 11.x, laravel-vite-plugin ^1.0, Vite 5.x, Node 20 LTS, deployed on Ubuntu 22.04 (also reproducible locally on Windows/WSL2).
The Error
Illuminate\Foundation\ViteException
Unable to locate file in Vite manifest: resources/css/app.css.
at vendor/laravel/framework/src/Illuminate/Foundation/Vite.php:410
406▕ protected function manifestHash(string $buildDirectory): ?string
407▕ {
408▕ ...
409▕ }
➜ 410▕ throw new ViteException("Unable to locate file in Vite manifest: {$path}.");
411▕
412▕ public function asset(string $asset, ?string $buildDirectory = null): string
Why It Happens
Laravel's @vite() directive doesn't read your source files directly in production — it reads public/build/manifest.json, a file that Vite generates when you run npm run build. That manifest maps each entry point (like resources/css/app.css) to its hashed, compiled filename (like app-4f9a2e1c.css).
This exception fires in one of three situations, and it's almost never actually a "Laravel" bug:
- You never ran
npm run buildon this environment, sopublic/build/doesn't exist at all. - You built the assets, but
public/buildwas excluded by.gitignore, a deploy script, or a Docker.dockerignore, so it never made it to the server. - The path you're asking for in Blade (
resources/css/app.css) doesn't match, character-for-character, an entry point listed invite.config.js.
Since the manifest is a static snapshot, any mismatch between what Blade requests and what Vite actually built throws this exact exception — Laravel has no fallback because in production it deliberately won't compile on the fly.
The Fix
Step 1 — Confirm the manifest actually exists on the server:
ls -la public/build/manifest.json
If that file doesn't exist, run the build on that environment:
$ npm install
$ npm run build
Step 2 — Match your Blade calls to your actual Vite entry points. Open vite.config.js and check the input array:
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
Then make sure your Blade layout calls @vite() with the exact same paths, in the exact same casing:
@vite(['resources/css/app.css', 'resources/js/app.js'])
Step 3 — If the manifest exists but deploy still fails, check your .gitignore and deploy pipeline. public/build is ignored by default in a fresh Laravel install, which is correct for git, but your CI/CD step must run npm run build as part of the deploy — it can't just git pull and expect compiled assets to appear.
Step-by-Step
- SSH into the environment throwing the error and run
ls public/build/manifest.jsonto confirm it's missing. - Run
npm install && npm run buildlocally or in your CI step, never by hand-editing files on the server. - Open
vite.config.jsand copy the exact strings from theinputarray. - Paste those exact strings into your
@vite([...])call in the Blade layout — don't retype them. - Check your deploy script (GitHub Actions, Forge, Envoyer, custom bash) and confirm a build step runs after the code is pulled and before the app goes live.
- Clear Laravel's view cache after deploying, since a cached compiled Blade view can hold a stale reference:
php artisan view:clear.
npm run dev locally and everything works, but production fails, check that APP_ENV isn't stuck on local in your production .env. When APP_ENV=local, Vite tries to connect to the dev server on localhost:5173 instead of reading the manifest — and if that dev server isn't running on your production box (it shouldn't be), you'll get connection errors that look similar to a manifest issue but have a completely different root cause.
Still stuck after checking all three causes above? Reach out on the Contact page and send me your vite.config.js — I'll take a look.