The problem: After upgrading to Inertia.js v3, your global error handling — the code that redirects on failed requests or shows a network error toast — silently stops running. No exception, no console warning, it just never fires anymore.
Environment: Laravel 11.x/13.x, Inertia.js v3.x (Laravel adapter + Vue 3 or React 19), Vite 7.x.
The Error
This is the dangerous kind of breaking change — there's nothing in the console to point you at it. The code compiles, runs, and does absolutely nothing when it should:
router.on('exception', (event) => {
console.error('Server error:', event.detail);
});
router.on('invalid', (event) => {
toast.error('Something went wrong');
});
// no error thrown — these listeners are just attached to
// event names that Inertia v3 no longer emits, ever
Why It Happens
Inertia v3 renamed two of its global router events as part of a broader push to make error handling clearer, and the old names were removed outright rather than kept as deprecated aliases:
'invalid' → renamed to 'httpException' (4xx/5xx responses)
'exception' → renamed to 'networkError' (request never reached the server)
Because JavaScript doesn't error on router.on() being called with a string it doesn't recognize as a valid event, your old listener registration just quietly does nothing — no crash, no warning, no hint in the terminal or browser console. The only way you'd notice is a user reporting that error pages or toasts stopped appearing, days or weeks after the upgrade.
This is one of several changes in v3 that fall into the same "compiles clean, breaks silently" category, alongside the removed future config block (harmless to leave in, but does nothing) and the restructured Inertia config file, which needs to be republished rather than left in its old shape.
The Fix
Search your JS codebase for the old event names before you assume the upgrade went cleanly:
$ grep -rn "router.on('invalid'" resources/js
$ grep -rn "router.on('exception'" resources/js
Rename each match to the v3 event name:
router.on('httpException', (event) => {
console.error('Server error:', event.detail);
});
router.on('networkError', (event) => {
toast.error('Something went wrong');
});
Prefer per-visit callbacks where it makes sense instead of a single global listener — v3 added onHttpException and onNetworkError options directly on individual requests, which is often clearer than a catch-all handler:
router.post('/users', data, {
onHttpException: (response) => { /* handle 4xx/5xx */ },
onNetworkError: (error) => { /* handle connection failure */ },
});
Republish the Inertia config and clear compiled views — the config file structure changed in v3, and the @inertia Blade directive's output changed too:
$ php artisan vendor:publish --provider="Inertia\ServiceProvider" --force
$ php artisan view:clear
Step-by-Step
- Before upgrading, confirm your baseline: Inertia v3's Laravel adapter requires PHP 8.2 and Laravel 11+; the React adapter requires React 19; the Svelte adapter requires Svelte 5 with runes syntax.
- Run
composer require inertiajs/inertia-laraveland update your client-side package (@inertiajs/vue3,@inertiajs/react, or@inertiajs/svelte) to the v3 line. - Grep your JS codebase for
router.on('invalid'androuter.on('exception', and rename every match tohttpExceptionandnetworkError. - Remove any leftover
future: { ... }block from yourcreateInertiaApp()setup — it's a no-op in v3 but worth cleaning up. - Update the
inertiaattribute in your root Blade<head>elements todata-inertia(e.g.<title data-inertia>). - Republish the Inertia config with
--force, review the new file for any customizations you need to re-apply, then runphp artisan view:clear. - If your app relies on Axios interceptors, reinstall
axiosdirectly (it's no longer bundled) and pass it in via the newhttpoption if you want to keep using it instead of the built-in client. - Test every page that shows error states or toasts specifically — this is the category of bug that won't show up until a real 404, 500, or dropped connection happens.
$props(), $state(), $effect()) touches every component file and is a real rewrite, not a mechanical find-and-replace — bundling it with the Inertia upgrade makes it much harder to isolate which change caused a regression if something breaks in staging.
Found another silent breaking change not listed here? Share it on the Contact page and I'll add it to the guide.