Laravel error
Laravel: Unable to locate file in Vite manifest
The error
Unable to locate file in Vite manifest: resources/js/app.jsThe @vite directive looked up an entry point in the build manifest and did not find it. Which of the four causes you have depends on whether the manifest exists at all.
The error
Illuminate\Foundation\ViteManifestNotFoundException
Unable to locate file in Vite manifest: resources/js/app.js.
Or, when nothing has been built at all:
Vite manifest not found at: /var/www/public/build/manifest.json
The two messages are worth telling apart, because they point at different halves of the problem. The first means a manifest exists and does not contain what you asked for. The second means there is no manifest.
What it means
In production, @vite(['resources/js/app.js']) does not serve that file. It
looks the path up in public/build/manifest.json - a map written by the build
from source paths to the hashed filenames that were actually produced - and
emits a tag pointing at the hashed name.
If the lookup fails, Blade has no URL to emit and throws rather than rendering a page with silently missing scripts.
In development none of this happens. npm run dev writes a hot file, Blade
notices it, and every asset request goes to the dev server instead. That is
why this error is so often a deployment error: the manifest path is exercised
for the first time on the server.
The four causes
Nothing was built. No npm run build in the deploy, or a build that failed
and whose exit code nobody checked. This is the "manifest not found" variant
and it is the most common by a wide margin.
npm ci
npm run buildnpm ci rather than npm install on a server: it installs exactly what the
lock file says instead of resolving afresh, which is the difference between a
reproducible deploy and one that behaves differently on a Tuesday.
The dev server is not running. In local development, npm run dev stopped
- or was never started - and there is no build either. Start it, or run a build.
The path does not match the entry point. The string in @vite() has to
match a key in the manifest exactly. resources/js/app.js and
/resources/js/app.js are different keys, and a file renamed in
vite.config.js without being renamed in the Blade layout produces exactly
this.
Read the manifest rather than guessing:
cat public/build/manifest.json | head -20The build output is not where the application is looking. A buildDirectory
set in the config, assets deployed to a different path, or a symlinked release
directory that was swapped after the build - all produce a correct manifest
that the running application cannot see.
In tests
A test suite rendering Blade views will hit this on any machine that has not run a build, including CI. Two ways out, and they suit different cases:
// for a specific test that does not care about assets
$this->withoutVite();// once, in TestCase::setUp, when no test does
protected function setUp(): void
{
parent::setUp();
$this->withoutVite();
}The alternative - building assets in CI before the test job - is right when you have tests that genuinely exercise the frontend, and wasteful when you do not.
Getting it right in the deploy
The order matters, and it is the same order that fixes most asset problems:
npm ci
npm run build # must succeed before anything else continues
php artisan migrate --force
php artisan config:cache
php artisan view:cacheCache views after the build, never before. A cached Blade view holds the compiled output including the asset tags, so caching first bakes in the previous build's hashes and you serve files that no longer exist - an error with a completely different message and the same root cause.
That ordering is part of a deploy that surprises nobody, and the rest of it is four steps long. The autoload map behaves the same way: build it at the wrong moment and the error names a class instead of a file.
