Fixed: Inertia.js "Must Receive a Valid Inertia Response" Error

The problem: A form submission or redirect suddenly throws All Inertia requests must receive a valid Inertia response, even though the route worked fine before.

Environment: Laravel 11, Inertia.js 1.x, Vue 3 (also applies to React adapter), PHP 8.3

The Error

● ● ●  console
Error: All Inertia requests must receive a valid Inertia response, however a
plain JSON response was received.

    at handleFinishedRequest (inertia.js:412)
    at handleResponse (inertia.js:389)

Why It Happens

Inertia expects every response from the server — including redirects, validation errors, and error pages — to come back as an Inertia response wrapped with the special X-Inertia header. When any of these things happen, Laravel breaks that contract:

1. A controller method returns response()->json(...) directly instead of Inertia::render(...) — common when a route was copy-pasted from an API controller.

2. A middleware, exception handler, or third-party package (auth packages, rate limiters) intercepts the request and returns a plain JSON error response — for example a 401 or 403 from a package that doesn't know Inertia exists.

3. You hit an unhandled exception in local/dev mode, and Laravel's default exception page renders full HTML instead of routing through Inertia's error page handling.

Inertia's frontend JS is strict on purpose — it needs the X-Inertia header and a JSON page-object shape to know how to swap components without a full page reload. Any deviation throws this exact error.

The Fix

First, check the controller action that's failing and make sure it renders through Inertia, not raw JSON:

app/Http/Controllers/ProjectController.php
- return response()->json(['project' => $project]);
+ return Inertia::render('Projects/Show', [
+     'project' => $project,
+ ]);

If the request is legitimately correct but a middleware is the culprit (auth guards, rate limiters returning JSON), tell Laravel's exception handler to convert those responses into Inertia-friendly redirects. Add this in your exception handler:

bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    $exceptions->respond(function (Response $response, Throwable $e, Request $request) {
        if ($request->header('X-Inertia') && in_array($response->getStatusCode(), [401, 403, 404, 419, 429, 500, 503])) {
            return back()->with('error', $e->getMessage());
        }
        return $response;
    });
})

For local dev exceptions specifically, make sure Ignition/Laravel's debug page isn't rendering on Inertia-tagged requests — that HTML response is the #1 cause during active development.

Step-by-Step

  1. Find the exact route/controller method that's failing using the Network tab — check its response headers for a missing X-Inertia header.
  2. If it's your own controller, swap any response()->json() call for Inertia::render().
  3. If it's middleware or a package, add the respond() handler shown above in bootstrap/app.php.
  4. Clear route and config cache: php artisan route:clear && php artisan config:clear.
  5. Reproduce the original action and confirm the response now includes the X-Inertia: true header in DevTools.
  6. Check validation-heavy forms specifically — a custom FormRequest with a manual failedValidation() override is a frequent hidden cause.

Edge case: If you're using Laravel Sanctum for API + Inertia in the same app, Sanctum's stateful API guard can return a plain JSON 401 for expired tokens on routes it thinks are API calls, even though the frontend is an Inertia page. Double-check your stateful domains config in config/sanctum.php — a missing domain entry silently breaks Inertia's response contract only on that route.

Hitting a stubborn Inertia response error you can't isolate? Message me on the Contact page and I'll help debug it.

Advertisement
Advertisement