Fixed: Inertia.js Form Errors Not Showing After Validation Fails

The problem: Submitting an Inertia form with invalid data correctly triggers a 422 on the backend, but the frontend's errors object stays empty and no validation messages ever render.

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

The Error

● ● ●  behavior (network tab confirms 422)
POST /projects 422 (Unprocessable Content)
Response body: { "message": "The name field is required.", "errors": { "name": [...] } }

...but in the Vue component:
form.errors.name === undefined

Why It Happens

Inertia has a very specific contract for how validation errors get from a 422 response into your form helper's reactive errors object — and if any link in that chain is broken, the errors exist in the raw HTTP response but never reach your component. The usual causes:

1. Not using Inertia's useForm() helper at all — if the form is submitted via a raw router.post() call or plain Axios instead of useForm().post(), there's no reactive errors object being managed for you; you'd need to manually read the error bag from the response event yourself.

2. Missing the X-Inertia header on the request — this typically happens if a custom Axios interceptor or a manually-configured request bypasses Inertia's own request pipeline, so Laravel's exception handler doesn't know to format the 422 the way Inertia's frontend expects (a redirect-back-with-errors instead of a raw JSON error response).

3. A custom FormRequest overrides failedValidation() and throws a different exception type or returns a plain JSON response manually, bypassing Laravel's default validation exception handling that Inertia's middleware is built to intercept.

The Fix

First, confirm the form actually uses Inertia's useForm() helper, which wires up reactive error handling automatically:

resources/js/Pages/Projects/Create.vue
import { useForm } from '@inertiajs/vue3';

const form = useForm({
    name: '',
});

function submit() {
    form.post('/projects');
}

Reference errors directly from the form object in the template — this reactivity is what breaks if the form wasn't created with useForm():

<input v-model="form.name" />
<div v-if="form.errors.name">{{ form.errors.name }}</div>

If a custom FormRequest overrides failedValidation(), make sure it still throws Laravel's standard exception rather than manually returning JSON:

app/Http/Requests/StoreProjectRequest.php
protected function failedValidation(Validator $validator)
{
    - throw new HttpResponseException(response()->json($validator->errors(), 422));
    + throw new ValidationException($validator);
}

Step-by-Step

  1. Confirm the form is built with useForm(), not a raw Axios/fetch call or plain router.post().
  2. Check the Network tab request headers for the submission and confirm X-Inertia: true is present.
  3. If using a custom FormRequest, check failedValidation() for any manual JSON response and replace it with the standard ValidationException throw.
  4. Confirm the input's error binding in the template reads from form.errors.fieldName exactly matching the validation rule's key.
  5. Resubmit the invalid form and confirm form.errors populates in Vue DevTools' component inspector.
  6. Check that error messages render and clear correctly on the next successful submission (Inertia clears errors automatically after a successful response).

Edge case: If your form fields are nested (e.g. an array of line items like items.0.name), the validation rule key and the form.errors lookup key must match exactly including the array index — a rule written as items.*.name in the FormRequest won't automatically map to form.errors['items.0.name'] unless your Blade/Vue error lookup also accounts for the specific index, which is easy to get wrong in dynamically-added form rows.

Still not seeing validation errors after checking all this? Reach out on the Contact page and I'll help debug your form setup.

Advertisement
Advertisement