Use when implementing error handling, exception management, error boundaries, or structured error responses across Laravel and React. Covers custom exceptions, API error formats, React Error Boundaries, form validation errors, and logging strategies.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Use when implementing error handling, exception management, error boundaries, or structured error responses across Laravel and React. Covers custom exceptions, API error formats, React Error Boundaries, form validation errors, and logging strategies.
Error Handling — Laravel + Inertia + React
Comprehensive patterns for handling errors across the full stack: Laravel exception handling, custom domain exceptions, API error formats, React Error Boundaries, Inertia error handling, and logging strategies.
1. Laravel Exception Handler Customization
Laravel 11+ uses bootstrap/app.php for exception handling configuration. Earlier versions use app/Exceptions/Handler.php.
Maintain a consistent JSON error structure across the entire API.
Standard Error Response Structure
{"error":"error_code_snake_case","message":"Human-readable description of what went wrong.","errors":{"field_name":["Validation message for this field."]},"meta":{"request_id":"uuid-here","timestamp":"2025-01-15T10:30:00Z"}}
State conflict (e.g., duplicate, insufficient inventory)
422
validation_error
Validation failed, domain rule violation
423
locked
Resource is locked for editing
429
rate_limited
Too many requests
500
server_error
Unexpected server error
503
service_unavailable
Maintenance or dependency outage
4. Validation Error Handling
FormRequest Validation with Inertia
Inertia automatically handles validation errors. When a FormRequest fails, Laravel redirects back with errors in the session. Inertia picks these up and makes them available via usePage().props.errors.
// app/Http/Requests/Order/StoreOrderRequest.phpclassStoreOrderRequestextendsFormRequest{
publicfunctionrules(): array{
return [
'customer_name' => ['required', 'string', 'max:255'],
'customer_email' => ['required', 'email', 'max:255'],
'items' => ['required', 'array', 'min:1'],
'items.*.product_id' => ['required', 'exists:products,id'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
];
}
publicfunctionmessages(): array{
return [
'items.required' => 'At least one item is required.',
'items.min' => 'At least one item is required.',
'items.*.product_id.exists' => 'The selected product does not exist.',
];
}
}
useIlluminate\Support\Facades\DB;
// Laravel handles deadlock retries automatically with the attempts parameter:
DB::transaction(function () {
// Critical sectionOrder::lockForUpdate()->find($orderId);
// ...
}, attempts: 3); // Retry up to 3 times on deadlock
// resources/js/Layouts/AuthenticatedLayout.tsximport { ErrorBoundary } from'@/Components/ErrorBoundary';
exportdefaultfunctionAuthenticatedLayout({ children }: { children: React.ReactNode }) {
return (
<divclassName="min-h-screen bg-gray-100"><nav>{/* Navigation */}</nav><ErrorBoundaryfallback={
<divclassName="max-w-7xl mx-auto py-12 px-4 text-center"><h2className="text-2xl font-bold">Page Error</h2><pclassName="mt-2 text-gray-600">
This section encountered an error. Your data is safe.
</p><ahref="/"className="mt-4 inline-block text-blue-600 hover:underline">
Return to Dashboard
</a></div>
}
>
<mainclassName="max-w-7xl mx-auto py-6 px-4">{children}</main></ErrorBoundary></div>
);
}
7. Inertia.js Error Handling
Inertia Error Pages
Configure Inertia to render custom error pages for HTTP errors:
// bootstrap/app.php (Laravel 11+)useInertia\Inertia;
useSymfony\Component\HttpFoundation\Response;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->respond(function (Response $response) {
$status = $response->getStatusCode();
if (in_array($status, [403, 404, 500, 503])) {
returnInertia::render('Error', [
'status' => $status,
'message' => match ($status) {
403 => 'You are not authorized to access this page.',
404 => 'The page you are looking for could not be found.',
500 => 'An unexpected server error occurred.',
503 => 'The service is temporarily unavailable.',
},
])->toResponse(request())->setStatusCode($status);
}
return$response;
});
})
useApp\Exceptions\OrderNotCancellableException;
useApp\Enums\OrderStatus;
useApp\Models\Order;
it('throws when cancelling a shipped order', function () {
$order = Order::factory()->shipped()->create();
$this->service->cancel($order);
})->throws(OrderNotCancellableException::class);
it('includes order id in exception', function () {
$order = Order::factory()->shipped()->create();
try {
$this->service->cancel($order);
$this->fail('Expected exception was not thrown.');
} catch (OrderNotCancellableException $e) {
expect($e->orderId)->toBe($order->id);
expect($e->getMessage())->toContain((string) $order->id);
}
});
Testing API Error Responses
it('returns 404 for nonexistent order', function () {
$this->actingAs(User::factory()->create())
->getJson('/api/orders/99999')
->assertNotFound()
->assertJson([
'error' => 'resource_not_found',
'message' => 'The requested resource was not found.',
]);
});
it('returns 422 with validation errors', function () {
$this->actingAs(User::factory()->create())
->postJson('/api/orders', [])
->assertUnprocessable()
->assertJsonValidationErrors(['customer_name', 'items']);
});
it('returns 403 for unauthorized access', function () {
$order = Order::factory()->create();
$this->actingAs(User::factory()->create())
->getJson("/api/orders/{$order->id}")
->assertForbidden();
});
Testing Transaction Rollback
it('rolls back on inventory failure', function () {
$product = Product::factory()->create(['stock' => 1]);
$dto = newOrderDTO(
// ...order data with quantity: 10 (more than available)
);
expect(fn () => $this->service->create($dto))
->toThrow(InsufficientInventoryException::class);
// Verify no order was created (transaction rolled back)$this->assertDatabaseCount('orders', 0);
// Verify stock was not decrementedexpect($product->fresh()->stock)->toBe(1);
});