About Error Handling in Laravel
web-application-framework laravel
I investigated error handling in Laravel and tried out how to log and handle responses when exceptions occur.
Reference
Environment
- Laravel 11
Table of Contents
Preparation
Before testing error handling, create a custom exception class that extends Laravel’s base Exception class.
Run the following Artisan command to generate the TestException class:
php artisan make:exception TestException
This will create the file: app/Exceptions/TestException.php.
Logging Exceptions with report
By default, exceptions are logged according to the project’s logging configuration.
This behavior can be customized in bootstrap/app.php or in individual Exception subclasses.
Defining in app.php
To define logging behavior in bootstrap/app.php, use the report method inside the closure passed to withExceptions.
Edit bootstrap/app.php as follows:
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
...
)
->withMiddleware(function (Middleware $middleware) {
...
})
->withExceptions(function (Exceptions $exceptions) {
// Add the following
$exceptions->report(function (TestException $e) {
// Log 'test'
Log::error('test');
});
})->create();
This configuration logs the message ‘test’ whenever a TestException is thrown.
You can specify the exception type using the closure’s type hint.
To disable the default logging behavior, either use stop() or return false from the closure:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->report(function (TestException $e) {
// Log 'test'
Log::error('test');
})->stop(); // Add stop()
})
->withExceptions(function (Exceptions $exceptions) {
$exceptions->report(function (TestException $e) {
// Log 'test'
Log::error('test');
return false; // Add 'return false'
});
})
Defining in the Exception Class
You can also define logging behavior within the exception class itself by overriding the report method.
In app/Exceptions/TestException.php, add:
...
class TestException extends Exception
{
// Add the following
public function report(): void
{
// Log 'test'
Log::error('test');
}
}
This logs ‘test’ whenever a TestException is thrown.
If you want to apply custom logic based on certain conditions, you can return true or false from the report method.
Returning true uses the custom logic; returning false falls back to the default handler.
public function report(Request $request): bool
{
$user = $request->user();
if ($user === null) {
// Use default handling if user is not authenticated
return false;
}
if ($user->id % 2 === 0) {
// Log if user ID is even
Log::error('test');
return true;
}
// Use default handling if user ID is odd
return false;
}
Customizing Responses with render
By default, Laravel inspects the HTTP request’s Accept header and automatically returns either an HTML or JSON response when an exception occurs.
You can customize this behavior in bootstrap/app.php or in individual exception classes.
Defining in app.php
Edit bootstrap/app.php and use the render method inside the closure passed to withExceptions.
app.php :
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
...
)
->withMiddleware(function (Middleware $middleware) {
...
})
->withExceptions(function (Exceptions $exceptions) {
// Add the following
$exceptions->render(function (TestException $e) {
return response()->json(['error_message' => $e->getMessage()]);
});
})->create();
This returns a JSON response with the error message when a TestException is thrown.
Make sure the render method returns an instance of Illuminate\Http\Response class or a subclass of it, typically created using the response helper.
If the closure returns nothing, Laravel will use its default response handling.
Defining in the Exception Class
You can override the render method in a custom exception class to define your own response logic.
In app/Exceptions/TestException.php, add:
...
class TestException extends Exception
{
// Add the following
public function render(): JsonResponse
{
return response()->json(['error_message' => $this->message]);
}
}
This returns a JSON response containing the exception message whenever the exception is thrown.
You can also use conditional logic in render to switch between custom and default responses by returning false:
public function report(Request $request): bool
{
$user = $request->user();
if ($user === null) {
// Use default response
return false;
}
if ($user->id % 2 === 0) {
// Custom response
return response()->json(['error_message' => $this->message]);
}
// Use default response for odd IDs
return false;
}