skip to content

In Laravel 13, which exceptions does the handler never report by default, and how do dontReport, ShouldntReport and stopIgnoring change that?

level: middleimportance: should knowfreq 40%

answer

  1. a built-in internal ignore list
  2. HttpException, so every abort() too
  3. validation, auth, missing models, 419 token
  4. ShouldntReport is a marker interface
  5. stopIgnoring removes the exact listed class

basics

~10 s

Laravel skips an internal list: HTTP exceptions (so any abort()), validation, authentication, authorization, missing-model, CSRF-token and origin-mismatch errors. dontReport() and the ShouldntReport interface add types, dontReportWhen() adds a condition, stopIgnoring() removes a listed class.

solid answer

~30 s

The handler keeps an internal ignore list: `HttpException` (so every `abort()`, even `abort(500)`), `HttpResponseException`, `ValidationException`, `AuthenticationException`, `AuthorizationException`, `ModelNotFoundException` and the record-not-found exceptions, `TokenMismatchException` (419), `OriginMismatchException`, `BackedEnumCaseNotFoundException` and Symfony's `RequestExceptionInterface`. The match is `instanceof`, so subclasses are skipped too. In `bootstrap/app.php`, `$exceptions->dontReport([...])` adds classes, `dontReportWhen(fn (Throwable $e) => ...)` skips when the closure returns `true`, and implementing `Illuminate\Contracts\Debug\ShouldntReport` marks a class as never reported. `stopIgnoring()` removes class names from both lists by exact name, so you must name the listed class: `stopIgnoring(HttpException::class)` reports every HTTP exception, 404s included. Ignored exceptions are still rendered normally.

code

php · 18 lines
php
<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use App\Exceptions\SmsQuotaExceededException;
use Symfony\Component\HttpKernel\Exception\HttpException;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(web: __DIR__.'/../routes/web.php', health: '/up')
    ->withExceptions(function (Exceptions $exceptions): void {
        $exceptions->dontReport(SmsQuotaExceededException::class);

        // Report abort(500)/abort(503), keep every 4xx quiet.
        $exceptions->stopIgnoring(HttpException::class);
        $exceptions->dontReportWhen(
            fn (Throwable $e) => $e instanceof HttpException && $e->getStatusCode() < 500
        );
    })->create();

go deeper

for a junior

Recall that validation errors, 404s and abort() calls are not logged by default, and that dontReport() adds your own classes.

for a middle

Explain the instanceof matching, the ShouldntReport marker, dontReportWhen's strict true, and why stopIgnoring needs the listed class name.

for a senior

Spot that abort(500) hides real outages from the log, and design rules that report 5xx HTTP exceptions without flooding on 404s.

for a principal

Decide which failure classes are operator signal and which are user noise, and keep that policy in one reviewed place.

## Why some exceptions are never reported Many exceptions in a Laravel app are **control flow**, not faults: a validation failure becomes a 422, a missing model becomes a 404, a guest hitting a protected page is redirected to login. Logging them would bury real errors, so the handler checks every exception against ignore rules before it reports anything. Ignoring affects **reporting only**; the exception is still rendered into its normal response. ## The internal list in Laravel 13 The handler's `$internalDontReport` property holds these classes, matched with `instanceof`, so subclasses count: | Class | Typical source | |---|---| | `Symfony\...\HttpException` | `abort()` with any status, `NotFoundHttpException` for unknown routes | | `Illuminate\Http\Exceptions\HttpResponseException` | Code that throws a ready-made response | | `Illuminate\Validation\ValidationException` | Failed validation | | `Illuminate\Auth\AuthenticationException` | Unauthenticated request | | `Illuminate\Auth\Access\AuthorizationException` | A denied gate or policy | | `ModelNotFoundException`, `RecordNotFoundException`, `RecordsNotFoundException` | `findOrFail()`, route model binding misses | | `Illuminate\Session\TokenMismatchException` | A 419 from a missing or stale CSRF token | | `Illuminate\Http\Exceptions\OriginMismatchException` | A cross-origin request rejected by `PreventRequestForgery` | | `BackedEnumCaseNotFoundException` | An enum route parameter with an unknown value | | Symfony `RequestExceptionInterface` | Malformed or suspicious requests | The first row has a trap: because the whole `HttpException` class is ignored, `abort(500, 'Payment backend down')` produces **no log entry**. Throw a real exception when you want a server fault recorded. ## Adding your own rules Three tools extend the list, all in `bootstrap/app.php` or on the class: - **`$exceptions->dontReport(SmsQuotaExceededException::class)`** accepts a class name or an array and adds it to the application's list. - **`$exceptions->dontReportWhen(fn (Throwable $e) => ...)`** registers a closure; the exception is skipped when it returns exactly `true`. Use it for conditions a class name cannot express, such as a status code or a reason field. - **`Illuminate\Contracts\Debug\ShouldntReport`** is a marker interface with no methods. An exception that implements it is never reported, and the decision travels with the class. ## Removing entries with stopIgnoring() `$exceptions->stopIgnoring(...)` takes a class name or an array and removes those names from both the application list and the internal list. The removal is an **exact string match** against the list entries, while the ignore check itself uses `instanceof`. That combination means: 1. `stopIgnoring(NotFoundHttpException::class)` changes nothing, because the list holds its parent `HttpException`, which still matches. 2. `stopIgnoring(HttpException::class)` re-enables reporting for **every** HTTP exception, 404s and 403s included. 3. To report only server-side aborts, pair it with a condition: ```php $exceptions->stopIgnoring(HttpException::class); $exceptions->dontReportWhen( fn (Throwable $e) => $e instanceof HttpException && $e->getStatusCode() < 500 ); ``` `stopIgnoring()` does not touch `ShouldntReport`: the interface is checked separately, so a class that implements it stays unreported whatever the lists say. ## Choosing between the tools | Tool | Where it lives | Best for | |---|---|---| | `dontReport([...])` | `bootstrap/app.php` | Third-party or framework classes you cannot edit | | `ShouldntReport` | On your exception class | Your own expected failures; the rule travels with the class | | `dontReportWhen(fn ...)` | `bootstrap/app.php` | Conditions: a status code, a reason code, an environment | | `stopIgnoring([...])` | `bootstrap/app.php` | Undoing a built-in or earlier ignore entry | A useful review question for each entry is "would an operator act on this?". An expected business outcome, such as a customer who opted out of SMS, is not operator signal and belongs on an ignore rule; a provider rejecting every message because an API key expired is signal and must never be silenced by a broad `dontReport` on the gateway's base exception class. ## How the checks are ordered Before reporting, the handler runs deduplication (when enabled), then `ShouldntReport`, then the combined lists, then `dontReportWhen` closures, then `throttle()`. An exception rejected at any step skips the exception's own `report()` method, every report callback and the logger. ## Common mistakes - Expecting `abort(500)` to appear in the log. - Calling `stopIgnoring()` on a subclass of a listed class. - Returning a truthy non-boolean from `dontReportWhen`; only `true` skips. - Assuming an ignored exception also gets a blank or default response; rendering is unaffected.

  • Why does stopIgnoring(NotFoundHttpException::class) not make 404s appear in the log?
    `stopIgnoring()` removes names from the lists by exact string match, and the internal list holds `HttpException`, not `NotFoundHttpException`. The ignore check uses `instanceof`, so a 404 still matches the parent. Remove `HttpException` instead, and add a `dontReportWhen` condition if you want only some statuses.
  • Is an exception in dontReport still turned into an error response?
    Yes. The ignore rules only affect reporting. The handler still renders the exception through render callbacks or its defaults, so the user sees the same page or JSON body as before.
  • Can stopIgnoring() re-enable a class that implements ShouldntReport?
    No. The handler checks the `ShouldntReport` interface on its own, before the lists, and `stopIgnoring()` only edits the lists. Remove the interface from the class, or report that case from a catch block with a different exception.

saying these in an interview costs you the question

  • abort(500) is always written to the log as an error
  • stopIgnoring() on a subclass re-enables reporting for just that subclass
  • Exceptions in dontReport are also hidden from the user's response
  • ShouldntReport is a trait that adds a dontReport() method
  • ValidationException is logged at warning level by default