Laravel Tutorial · Requests and Responses · Lesson 12 of 27
Errors, Exceptions and Logging
Stop requests with abort(), throw custom exceptions that render their own responses, report errors to logs, and know what debug mode changes.
abort and HTTP exceptions
abort(403), abort(404, 'Not found'), abort_if($cond, 403) and abort_unless(...) throw an HTTP exception that Laravel converts into a response: a JSON error for API requests, an error page for browsers (resources/views/errors/404.blade.php customises it).
Custom exceptions
An exception class can define render() (what the user gets) and report() (what's logged). Or configure rendering centrally in bootstrap/app.php with ->withExceptions(fn ($exceptions) => $exceptions->render(...)).
<?php
use Illuminate\Support\Facades\Route;
class InsufficientStock extends RuntimeException
{
public function __construct(public string $sku, public int $available)
{
parent::__construct("Not enough stock for $sku");
}
public function render()
{
return response()->json([
'error' => 'insufficient_stock',
'message' => "Only {$this->available} left of {$this->sku}.",
], 409);
}
}
$stock = ['MUG' => 2];
Route::post('/cart/{sku}', function (string $sku) use ($stock) {
abort_unless(isset($stock[$sku]), 404, "Unknown product $sku");
$qty = (int) request('qty', 1);
if ($qty > $stock[$sku]) {
throw new InsufficientStock($sku, $stock[$sku]);
}
return ['added' => $qty];
});
Route::get('/admin', fn () => abort(403));
Route::get('/broken', fn () => throw new LogicException('database password is hunter2'));
visit('POST', '/cart/MUG', ['qty' => 1]);
visit('POST', '/cart/MUG', ['qty' => 5]);
visit('POST', '/cart/TEA');
visit('GET', '/admin');
visit('GET', '/broken'); // debug mode is off: the message is hidden from the client
Output
POST /cart/MUG -> 200 {"added":1}
POST /cart/MUG -> 409 {"error":"insufficient_stock","message":"Only 2 left of MUG."}
POST /cart/TEA -> 404 {
"message": "Unknown product TEA"
}
GET /admin -> 403 {
"message": ""
}
GET /broken -> 500 {
"message": "Server Error"
}Run this on your computer — see “Get Started” for setup.
Debug mode and logging
APP_DEBUG=trueshows detailed error pages with stack traces: never in production, it leaks code and secrets.report($e)logs an exception without stopping the request;Log::error('message', ['order' => $id])writes structured log entries to the channel configured inconfig/logging.php(files, stderr, Slack, external services).
Test yourself
Why must APP_DEBUG be false in production?
It makes the app slowerDebug error pages expose stack traces, code and configuration detailsIt disables loggingRoutes stop working
Show answer
Answer: Debug error pages expose stack traces, code and configuration details