> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Fehlerbehandlung

> Lernen Sie die Ausnahmebehandlung von Laravel kennen: von Reporting und Rendering über eigene Fehlerseiten bis zur Erzeugung von HTTP-Fehlerantworten.

## Überblick

Wenn Sie ein neues Laravel-Projekt anlegen, sind Fehler- und Ausnahmebehandlung bereits vorkonfiguriert.
Anpassungen nehmen Sie über die Methode `withExceptions` in `bootstrap/app.php` vor.

### Ablauf der Ausnahmebehandlung

Vom Auslösen einer Ausnahme bis zur Antwort an den Client durchläuft Laravel folgende Schritte:

```mermaid theme={null}
flowchart TD
    A["Ausnahme wird ausgelöst"] --> B["ExceptionHandler::report"]
    B --> C{"Loggen?"}
    C -->|"Ja"| D["Log-Eintrag schreiben"]
    C -->|"Nein"| E["Überspringen"]
    D --> F["ExceptionHandler::render"]
    E --> F
    F --> G{"Request-Typ"}
    G -->|"Web"| H["HTML-Fehlerseite"]
    G -->|"API/JSON"| I["JSON-Fehlerantwort"]
    H --> J["An Client senden"]
    I --> J
```

```php theme={null}
// bootstrap/app.php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;

return Application::configure(basePath: dirname(__DIR__))
    ->withExceptions(function (Exceptions $exceptions): void {
        // Reporting und Rendering von Ausnahmen hier konfigurieren
    })->create();
```

Das an die `withExceptions`-Closure übergebene Objekt `$exceptions` ist eine Instanz von `Illuminate\Foundation\Configuration\Exceptions` und verwaltet die gesamte Ausnahmebehandlung Ihrer Anwendung.

### Debug-Einstellungen

Die Option `debug` in `config/app.php` steuert, wie viele Fehlerinformationen ausgegeben werden.
Standardmäßig übernimmt sie den Wert der Umgebungsvariable `APP_DEBUG` aus der `.env`-Datei.

```ini theme={null}
# Lokale Entwicklung
APP_DEBUG=true

# Produktion
APP_DEBUG=false
```

<Warning>
  Setzen Sie `APP_DEBUG` in der Produktion unbedingt auf `false`. Andernfalls können sensible Informationen an Endnutzer gelangen.
</Warning>

## Ausnahmen melden

Beim Reporting wird eine Ausnahme protokolliert oder an externe Dienste wie [Sentry](https://github.com/getsentry/sentry-laravel) oder [Flare](https://flareapp.io) gesendet.
Standardmäßig wird gemäß den Einstellungen in `config/logging.php` in ein Log geschrieben.

### Eigene Report-Callbacks

Möchten Sie je nach Ausnahmetyp unterschiedliche Report-Logik ausführen, übergeben Sie eine Closure an die Methode `report`.
Laravel erkennt den Ausnahmetyp anhand der Type-Hints der Closure.

```php theme={null}
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // Benachrichtigung an externen Dienst usw.
    });
})
```

Auch wenn Sie eine eigene Callback registrieren, wird das Standard-Logging weiter ausgeführt.
Um die Standard-Propagation abzubrechen, rufen Sie `stop()` auf oder geben Sie `false` zurück.

```php theme={null}
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->report(function (InvalidOrderException $e) {
        // ...
    })->stop();
})
```

### Helper `report()`

Wenn Sie eine Ausnahme ausschließlich melden möchten, ohne eine Fehlerseite anzuzeigen, verwenden Sie den Helper `report()`.

```php theme={null}
public function isValid(string $value): bool
{
    try {
        // Validierung ...
    } catch (Throwable $e) {
        report($e);

        return false;
    }
}
```

<Tip>
  Mit dem Helper `report()` protokollieren Sie Fehler, ohne die Nutzerantwort zu unterbrechen. Ideal für nebensächliche Ausnahmen in Hintergrund-Jobs.
</Tip>

### Doppelte Reports verhindern

Wird dieselbe Ausnahme-Instanz mehrfach an `report()` übergeben, entstehen doppelte Log-Einträge.
Mit `dontReportDuplicates()` protokolliert Laravel dieselbe Instanz nur beim ersten Mal.

```php theme={null}
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportDuplicates();
})
```

```php theme={null}
$original = new RuntimeException('Whoops!');

report($original); // Wird protokolliert

try {
    throw $original;
} catch (Throwable $caught) {
    report($caught); // Wird ignoriert (dieselbe Instanz)
}
```

### Globaler Log-Kontext

Um allen Log-Einträgen zu Ausnahmen dieselben Informationen anzuhängen, verwenden Sie `context`.
Die aktuell angemeldete Benutzer-ID wird, sofern verfügbar, automatisch mitgegeben.

```php theme={null}
->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->context(fn () => [
        'app_version' => config('app.version'),
    ]);
})
```

### `context()`-Methode in der Ausnahmeklasse

Definieren Sie in einer eigenen Ausnahmeklasse die Methode `context()`, um spezifische Kontextinformationen an das Log zu übergeben.

```php theme={null}
<?php

namespace App\Exceptions;

use Exception;

class InvalidOrderException extends Exception
{
    public function __construct(
        private readonly int $orderId,
        string $message = '',
    ) {
        parent::__construct($message);
    }

    /**
     * Kontextinformationen der Ausnahme zurückgeben.
     *
     * @return array<string, mixed>
     */
    public function context(): array
    {
        return ['order_id' => $this->orderId];
    }
}
```

### Log-Level anpassen

Um eine bestimmte Ausnahme mit einem bestimmten Log-Level zu erfassen, verwenden Sie die Methode `level`.

```php theme={null}
use PDOException;
use Psr\Log\LogLevel;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->level(PDOException::class, LogLevel::CRITICAL);
})
```

### Reports drosseln

Wenn massenhaft Ausnahmen auftreten, können Sie die Reports mit `throttle` steuern.

```php theme={null}
use Illuminate\Support\Lottery;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    // Nur zufällig 1 von 1000 Fällen melden
    $exceptions->throttle(function (Throwable $e) {
        return Lottery::odds(1, 1000);
    });
})
```

Für eine Begrenzung pro Minute verwenden Sie `Limit`.

```php theme={null}
use Illuminate\Broadcasting\BroadcastException;
use Illuminate\Cache\RateLimiting\Limit;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->throttle(function (Throwable $e) {
        if ($e instanceof BroadcastException) {
            return Limit::perMinute(300);
        }
    });
})
```

## Ausnahmen rendern

Beim Rendering wird eine Ausnahme in eine HTTP-Antwort umgewandelt.
Standardmäßig erzeugt Laravel eine passende Antwort automatisch; Anpassungen sind aber möglich.

### Eigene Render-Callbacks

Übergeben Sie an die Methode `render` eine Closure, um die Ausnahme in eine Antwort umzuwandeln.

```php theme={null}
use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', status: 500);
    });
})
```

Auch das Rendering integrierter Ausnahmen (etwa `NotFoundHttpException`) lässt sich überschreiben.
Liefert die Closure keinen Wert, wird das Standardrendering verwendet.

```php theme={null}
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Record not found.',
            ], 404);
        }
        // null zurückgeben → Standard-404-Seite anzeigen
    });
})
```

### Automatische Erkennung von JSON/HTML

Laravel entscheidet anhand des `Accept`-Headers, ob HTML oder JSON zurückgegeben wird.
Um diese Logik anzupassen, nutzen Sie `shouldRenderJsonWhen`.

```php theme={null}
use Illuminate\Http\Request;
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
        if ($request->is('admin/*')) {
            return true; // Adminbereich immer als JSON
        }

        return $request->expectsJson();
    });
})
```

### Antwort insgesamt anpassen

Mit `respond` können Sie die erzeugte Antwort weiter verändern.

```php theme={null}
use Symfony\Component\HttpFoundation\Response;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->respond(function (Response $response) {
        if ($response->getStatusCode() === 419) {
            return back()->with([
                'message' => 'Die Seite ist abgelaufen. Bitte versuchen Sie es erneut.',
            ]);
        }

        return $response;
    });
})
```

## Eigene Ausnahmeklassen

Sie können in `app/Exceptions/` eigene Ausnahmeklassen anlegen.
Definieren Sie in der Klasse Methoden `report()` und `render()`, werden sie automatisch aufgerufen – auch ohne Konfiguration in `bootstrap/app.php`.

### Ausnahmeklasse erzeugen

<Steps>
  <Step title="Ausnahmeklasse erstellen">
    ```shell theme={null}
    php artisan make:exception InvalidOrderException
    ```
  </Step>

  <Step title="report() und render() implementieren">
    ```php theme={null}
    <?php

    namespace App\Exceptions;

    use Exception;
    use Illuminate\Http\Request;
    use Illuminate\Http\Response;

    class InvalidOrderException extends Exception
    {
        public function __construct(
            private readonly int $orderId,
            string $message = 'Invalid order.',
        ) {
            parent::__construct($message);
        }

        /**
         * Ausnahme melden.
         */
        public function report(): void
        {
            // Benachrichtigung an externen Dienst usw.
        }

        /**
         * Ausnahme in eine HTTP-Antwort umwandeln.
         */
        public function render(Request $request): Response
        {
            return response()->view('errors.invalid-order', [
                'orderId' => $this->orderId,
            ], 422);
        }
    }
    ```
  </Step>
</Steps>

<Info>
  Die `report()`-Methode unterstützt Dependency Injection per Type-Hint. Der Service Container von Laravel löst die Abhängigkeiten automatisch auf.
</Info>

### Interface `ShouldntReport`

Für Ausnahmen, die nicht gemeldet werden sollen, implementieren Sie das Interface `ShouldntReport`.
Solche Ausnahmen werden nie gemeldet.

```php theme={null}
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Contracts\Debug\ShouldntReport;

class PodcastProcessingException extends Exception implements ShouldntReport
{
    //
}
```

## Ausnahmen auslösen

### Helper `abort()`

Sie können von überall in der Anwendung eine HTTP-Fehlerantwort auslösen.

```mermaid theme={null}
flowchart LR
    A["abort(404)"] --> B["NotFoundHttpException"]
    C["abort(403)"] --> D["AccessDeniedHttpException"]
    E["abort(500)"] --> F["HttpException<br>500"]
    G["abort(422)"] --> H["UnprocessableEntityHttpException"]
    B --> I["resources/views/errors/404.blade.php"]
    D --> J["resources/views/errors/403.blade.php"]
    F --> K["resources/views/errors/500.blade.php"]
    H --> L["resources/views/errors/422.blade.php"]
```

```php theme={null}
// 404 Not Found
abort(404);

// Mit Nachricht
abort(403, 'Sie sind nicht berechtigt, diese Aktion auszuführen.');
```

### `abort_if()` / `abort_unless()`

Helfer, um Ausnahmen bedingt auszulösen.

```php theme={null}
// Wenn $condition true ist, abbrechen
abort_if(! $user->isAdmin(), 403);

// Wenn $condition false ist, abbrechen
abort_unless($user->hasPermission('edit'), 403, 'Permission denied.');
```

<Tip>
  Praktisch bei Berechtigungsprüfungen in Controllern oder Middleware, häufig in Kombination mit Gates und Policies.
</Tip>

## Globale Steuerung von Ausnahmen

### Ausnahmen gezielt ignorieren

Über `dontReport` bestimmen Sie Ausnahmen, die nicht gemeldet werden sollen. Individuelles Rendering bleibt weiter aktiv.

```php theme={null}
use App\Exceptions\InvalidOrderException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReport([
        InvalidOrderException::class,
    ]);
})
```

Für bedingtes Ignorieren übergeben Sie eine Closure an `dontReportWhen`.

```php theme={null}
use Throwable;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->dontReportWhen(function (Throwable $e) {
        return $e instanceof PodcastProcessingException &&
               $e->reason() === 'Subscription expired';
    });
})
```

<Info>
  Standardmäßig ignoriert Laravel bereits einige Ausnahmen wie 404-Fehler, CSRF-Token-Verstöße (419) und Origin-Konflikte (403).
</Info>

### Standardmäßig ignorierte Ausnahmen wieder melden

Um Ausnahmen, die Laravel standardmäßig ignoriert, wieder zu melden, verwenden Sie `stopIgnoring`.

```php theme={null}
use Symfony\Component\HttpKernel\Exception\HttpException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->stopIgnoring(HttpException::class);
})
```

## HTTP-Fehlerseiten

Zu jedem HTTP-Statuscode können Sie eigene Fehlerseiten definieren.

### Eigene Fehler-Views erstellen

Legen Sie im Verzeichnis `resources/views/errors/` Blade-Vorlagen an, deren Dateiname dem Statuscode entspricht.

```
resources/
└── views/
    └── errors/
        ├── 404.blade.php
        ├── 403.blade.php
        └── 500.blade.php
```

Innerhalb der View steht die Variable `$exception` mit den Fehlerdetails zur Verfügung.

```blade theme={null}
{{-- resources/views/errors/404.blade.php --}}
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Seite nicht gefunden</title>
</head>
<body>
    <h1>404 – Seite nicht gefunden</h1>
    <p>{{ $exception->getMessage() }}</p>
    <a href="{{ url('/') }}">Zur Startseite</a>
</body>
</html>
```

### Standard-Fehlervorlagen veröffentlichen

Wenn Sie die Standard-Fehlerseiten von Laravel als Ausgangspunkt verwenden möchten, holen Sie sie mit `vendor:publish`.

```shell theme={null}
php artisan vendor:publish --tag=laravel-errors
```

### Fallback-Fehlerseiten

Als Fallback für Statuscodes ohne eigene View können Sie `4xx.blade.php` und `5xx.blade.php` anlegen.

```
resources/
└── views/
    └── errors/
        ├── 4xx.blade.php  # Fallback für 4xx
        └── 5xx.blade.php  # Fallback für 5xx
```

<Warning>
  Für `404`, `500` und `503` liefert Laravel bereits eigene Standardseiten. Wenn Sie diese anpassen möchten, legen Sie statt eines Fallbacks eigene Dateien (`404.blade.php` usw.) an.
</Warning>

## Praxisbeispiel: API-Ausnahmehandler

Bei APIs möchten Sie Ausnahmen immer als JSON zurückgeben.
Nachfolgend ein Beispiel, wie Sie API-Fehler zentral in `bootstrap/app.php` verwalten.

```php theme={null}
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

->withExceptions(function (Exceptions $exceptions): void {
    // API-Requests immer als JSON beantworten
    $exceptions->render(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Ressource nicht gefunden.',
            ], 404);
        }
    });

    $exceptions->render(function (AuthenticationException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Authentifizierung erforderlich.',
            ], 401);
        }
    });

    $exceptions->render(function (ValidationException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Validierungsfehler.',
                'errors'  => $e->errors(),
            ], 422);
        }
    });
})
```

### Eigene API-Ausnahmeklasse

Eine gemeinsame Basisklasse für API-Ausnahmen ermöglicht konsistente Fehlerantworten aus jedem Endpunkt.

```php theme={null}
<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class ApiException extends Exception
{
    public function __construct(
        string $message = 'An error occurred.',
        private readonly int $statusCode = 500,
        private readonly array $errors = [],
    ) {
        parent::__construct($message);
    }

    public function render(Request $request): JsonResponse
    {
        $data = ['message' => $this->getMessage()];

        if (! empty($this->errors)) {
            $data['errors'] = $this->errors;
        }

        return response()->json($data, $this->statusCode);
    }
}
```

Verwendung im Controller:

```php theme={null}
<?php

namespace App\Http\Controllers\Api;

use App\Exceptions\ApiException;
use App\Models\Order;

class OrderController extends Controller
{
    public function show(int $id): JsonResponse
    {
        $order = Order::find($id);

        if (! $order) {
            throw new ApiException('Bestellung nicht gefunden.', 404);
        }

        if ($order->isCancelled()) {
            throw new ApiException('Diese Bestellung wurde bereits storniert.', 422);
        }

        return response()->json($order);
    }
}
```

## Zusammenfassung

<AccordionGroup>
  <Accordion title="Reporting im Überblick">
    | Vorgehen                   | Zweck                                                 |
    | -------------------------- | ----------------------------------------------------- |
    | `$exceptions->report()`    | Eigene Report-Logik pro Ausnahmetyp registrieren      |
    | `$exceptions->context()`   | Allen Ausnahme-Logs gemeinsame Informationen anhängen |
    | `context()`-Methode        | Ausnahmeklasse liefert eigenen Kontext                |
    | Helper `report()`          | Ausnahme melden, ohne die Antwort zu unterbrechen     |
    | `dontReportDuplicates()`   | Doppelte Reports derselben Instanz vermeiden          |
    | Interface `ShouldntReport` | Ausnahmeklassen erzeugen, die nie gemeldet werden     |
  </Accordion>

  <Accordion title="Rendering im Überblick">
    | Vorgehen                 | Zweck                                        |
    | ------------------------ | -------------------------------------------- |
    | `$exceptions->render()`  | Eigene Antwort pro Ausnahmetyp               |
    | `render()`-Methode       | Rendering-Logik direkt in der Ausnahmeklasse |
    | `shouldRenderJsonWhen()` | Erkennung JSON/HTML anpassen                 |
    | `respond()`              | Erzeugte Antwort weiter verändern            |
  </Accordion>

  <Accordion title="HTTP-Fehlerseiten im Überblick">
    * Erstellen Sie Dateien wie `resources/views/errors/404.blade.php` – Laravel nutzt sie automatisch.
    * Über die Variable `$exception` erhalten Sie die Fehlerdetails.
    * Mit `php artisan vendor:publish --tag=laravel-errors` holen Sie sich die Standardvorlagen.
    * `4xx.blade.php` / `5xx.blade.php` dienen als Fallback-Seiten.
  </Accordion>

  <Accordion title="Best Practices für die Produktion">
    * Setzen Sie `APP_DEBUG=false` und blenden Sie Stacktraces vor Nutzern aus.
    * Binden Sie externe Error-Tracker wie Sentry oder Flare an und verwalten Sie Fehler zentral.
    * Nutzen Sie `throttle()`, um Log-Fluten bei massenhaften Ausnahmen zu vermeiden.
    * Halten Sie bei API-Endpunkten ein konsistentes JSON-Fehlerformat ein.
  </Accordion>
</AccordionGroup>


## Related topics

- [Events und Listener](/de/events.md)
- [Laravel Fetch Metadata](/de/packages/laravel-fetch-metadata.md)
- [OAuth-2.0-Authentifizierung - Google Sheets API for Laravel](/de/packages/laravel-google-sheets/oauth.md)
- [Laravel Horizon](/de/horizon.md)
- [Session-Hooks](/de/packages/laravel-copilot-sdk/hooks.md)
