isaidgitmenow / laravel-errors
Cure your Laravel exception handling headaches. Manage errors, rate limits, and API responses using clean, declarative PHP 8.4 attributes.
Requires
- php: ^8.4
- illuminate/contracts: ^11.0|^12.0|^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.9
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
- phpunit/phpunit: ^11.0|^12.0|^13.0
Suggests
- barryvdh/laravel-debugbar: Required for Debugbar integration.
- filament/filament: Required for Filament panel integration.
- inertiajs/inertia-laravel: Required for Inertia.js integration.
- livewire/livewire: Required for Livewire component integration.
- sentry/sentry-laravel: Required for Sentry integration.
- spatie/laravel-ignition: Required for Ignition debug mode integration.
Provides
None
Conflicts
None
Replaces
None
README
Stop wrestling with bootstrap/app.php and bloated Exception classes. Laravel Errors completely reimagines exception handling in Laravel 11+ using PHP 8.4 Attributes.
Instead of writing custom render() and report() methods for every exception, or cluttering your global handler with complex if ($e instanceof ...) logic, you can now define how an exception behaves declaratively:
use Isaidgitmenow\LaravelErrors\Attributes\HttpCode; use Isaidgitmenow\LaravelErrors\Attributes\ReportTo; use Isaidgitmenow\LaravelErrors\Attributes\RateLimit; #[HttpCode(402)] #[ReportTo(['slack', 'sentry'])] #[RateLimit(max: 5, intervalInMinutes: 1)] class PaymentFailedException extends Exception { // That's it! No render(), no report() methods. }
๐ Why adopt this package today?
- Zero Boilerplate: Ditch the massive
renderandreportmethods. Your exceptions become clean, SOLID-compliant data structures. - Context-Aware Rendering: Throw an error anywhere. The package automatically detects if the request is from Livewire, Filament, Inertia, an API, or Web, and formats the response perfectly so your frontend doesn't break.
- Save Your Error Quotas: Native Rate Limiting (
#[RateLimit]) prevents error spikes from exhausting your Sentry/Flare limits. - Automatic Context Injection: Attach data via
#[WithContext]and it flows automatically into Laravel's globalContext, your logs, and your bug trackers. - Privacy Built-in: Use
#[Sensitive]to automatically redact passwords and API keys before they hit your logs.
composer require isaidgitmenow/laravel-errors
โจ Features
- Declarative PHP 8 Attributes: Configure HTTP codes, reporting rules, rate limiting, and context directly on your exception classes.
- Plug-and-Play Frontend Integrations: Automatically detects the context of the request (Filament, Livewire, Inertia, API, Web) and formats the response appropriately so your SPA or Admin panel doesn't break on a 500 error.
- Bulletproof Resilience: Includes a self-healing
try/catchwrapper so an error in your error handler never causes a White Screen of Death (WSOD). - Deep Attribute Inspection: Safely traverses Laravel's wrapped exceptions (e.g.,
QueryException,ViewException) to find and apply your custom attributes on the original exception. - Spatie Ignition & Laravel Debugbar Ready: Seamlessly integrates with local developer tools without breaking production flows.
- Xdebug IDE Enrichment: Push sanitized
#[WithContext]payloads directly to your IDE as debug notifications. - RFC 9457 Problem Details: The API Renderer automatically formats errors according to the IETF RFC 9457 standard (Problem Details for HTTP APIs).
- ULID Error Identities: Every error automatically generates a unique, sortable ULID (
ErrorIdentity) that is injected into logs and the API response for exact tracing. - Auto-Injection into Laravel Context: Automatically forwards
#[WithContext]data to downstream trackers like Sentry or Flare via Laravel 11's globalContext. - Deep Data Masking (
Masker): Built-in redaction for sensitive keys using the#[Sensitive]attribute (supportsfirst_last,last4,email,hash, andlengthmasking) before they hit logs or external trackers. - Anti-Spam Rate Limiting: Prevent cascading failures from exhausting your error tracker quotas using the
#[RateLimit]attribute. - Octane Compatible: Automatically flushes the reflection cache and dynamic state after every request under Swoole / RoadRunner to prevent memory leaks.
- Dynamic Pass-Through: Third-party packages can register exceptions to bypass the pipeline at runtime โ no config edits required.
- Critical Fallback Logging (
CriticalLog): Ensures your application never runs completely blind. If all reporters fail or none are configured, it writes to a dedicatedstorage/logs/errors-critical.log. - Environment-Specific Reporting: Restrict
#[ReportTo]to specific environments (e.g., only send Slack alerts inproduction). make:errorArtisan Command: Scaffold fully decorated exception classes in seconds withphp artisan make:error.- Static Analysis Ready: Ships with a
phpstan.neon.distpre-configured for Larastan level 8. - CI/CD Ready: Includes a GitHub Actions workflow matrix covering PHP 8.4/8.5 ร Laravel 11/12/13.
๐ฆ Requirements
- PHP 8.4+
- Laravel 11.0+
๐ Installation & Setup
You can install the package via composer:
composer require isaidgitmenow/laravel-errors
Optionally, publish the configuration file to customize the pipeline:
php artisan vendor:publish --tag="laravel-errors-config"
Integration is incredibly simple. Open your bootstrap/app.php and register the handler:
// bootstrap/app.php use Illuminate\Foundation\Configuration\Exceptions; use Isaidgitmenow\LaravelErrors\ErrorHandler; return Application::configure(basePath: dirname(__DIR__)) // ... ->withExceptions(function (Exceptions $exceptions) { // Let the package orchestrate your entire error pipeline: ErrorHandler::handle($exceptions); })->create();
โก Quick Start: 2 Error Archetypes
Exception handling boils down to one fundamental decision: Is it a simple client error, or a monitored error requiring the error object?
1๏ธโฃ When you need a SIMPLE ERROR (Client mistake / 4xx)
Decision: A client-side mistake occurred (validation failed, resource not found, forbidden action). You want to return the correct HTTP status and clean message to the client without polluting your Slack or Sentry with false alarms.
1. Run the command:
php artisan make:error ActionRejected --type=simple --http=400
2. What it generates (app/Exceptions/ActionRejected.php):
namespace App\Exceptions; use Exception; use Isaidgitmenow\LaravelErrors\Attributes\DontReport; use Isaidgitmenow\LaravelErrors\Attributes\HttpCode; #[HttpCode(400)] #[DontReport] // Client error: safe for client, suppressed from Sentry/Slack class ActionRejected extends Exception { public function __construct( // [1] Safe public message returned to the client / UI: string $message = 'The requested action could not be completed.', // [2] Add custom client-safe parameters here: // public readonly ?string $field = null, ) { parent::__construct($message); } }
3. Throw it in your Controller, Action, or Service:
throw new ActionRejected('The requested action could not be completed.');
๐ What happens automatically (Result):
- Client (API / Web): Receives a clean HTTP
400error response:{ "message": "The requested action could not be completed.", "code": "HTTP_400", "status": 400, "error_id": "01JAX6M78N0B1C2D3E4F5G6H7J", "errors": [] } - Inertia / Livewire / Filament: Displays the notification without crashing component state.
- Slack & Sentry: Completely silent (no log noise, zero alert fatigue).
2๏ธโฃ When you have an ERROR OBJECT and need MONITORING (Server / External / $previous)
Decision: An internal failure occurred or an external service failed. A raw error object
$earrived from atry/catch (\Throwable $e). You need to pass that error object forward ($previous: $e), attach debug context, mask secrets, and alert Slack/Sentry.
1. Run the command:
php artisan make:error OperationFailed --type=monitored --report=slack --env=production
2. What it generates (app/Exceptions/OperationFailed.php):
namespace App\Exceptions; use Exception; use Throwable; use Isaidgitmenow\LaravelErrors\Attributes\HttpCode; use Isaidgitmenow\LaravelErrors\Attributes\ReportTo; use Isaidgitmenow\LaravelErrors\Attributes\RateLimit; use Isaidgitmenow\LaravelErrors\Attributes\WithContext; use Isaidgitmenow\LaravelErrors\Attributes\Sensitive; #[HttpCode(500)] #[ReportTo('slack', environments: ['production'])] #[RateLimit(max: 5, intervalInMinutes: 1)] // Prevents alert floods #[WithContext] // Promoted constructor properties below are auto-logged and reported class OperationFailed extends Exception { public function __construct( // [1] CONTEXT PARAMETERS: // Public properties are auto-captured by #[WithContext] and sent to logs/Slack/Sentry. // Add your custom parameters here. Use #[Sensitive] to mask secrets: public int $entityId, #[Sensitive('last4')] public string $secretToken = '', // [2] SAFE PUBLIC MESSAGE: // Message displayed to the client / UI response (masks internal server details): string $message = 'An unexpected error occurred.', // [3] INCOMING ERROR OBJECT ($previous): // Pass the caught exception when wrapping inside catch (\Throwable $e): // Usage: throw new self(message: '...', previous: $e); ?Throwable $previous = null, ) { parent::__construct($message, previous: $previous); } }
3. Catch the incoming error object and pass it forward:
try { $thirdPartyService->execute(); } catch (\Throwable $e) { // <-- Raw incoming error object // Pass it forward via $previous so root cause and PHP stack trace are preserved: throw new OperationFailed( entityId: 101, secretToken: 'sk_live_abc123456789', previous: $e // <-- Preserves the root cause exception object ); }
๐ What happens automatically (Result):
- Client (API / Frontend): Receives a sanitized HTTP 500 error response with a tracking
error_idreference (never leaks secrets or stack traces):{ "message": "An unexpected error occurred.", "code": "HTTP_500", "status": 500, "error_id": "01JAX7K89M2N3P4Q5R6S7T8U9V", "errors": [] } - Slack, Sentry & Logs: Receive full forensic context with
#[Sensitive]masked and the original root-cause exception attached:{ "error_id": "01JAX7K89M2N3P4Q5R6S7T8U9V", "error_code": "HTTP_500", "original_message": "Connection timed out after 5000ms", "error_context": { "entityId": 101, "secretToken": "sk_live_****6789" } } - Anti-Spam: If 500 requests fail within a minute, Slack receives at most 5 alerts per minute thanks to
#[RateLimit].
๐ก Quick Comparison
| Decision | Command Archetype | What Client Receives | What Gets Sent to Slack / Sentry |
|---|---|---|---|
| Simple Error | --type=simple |
Safe HTTP status + clean message | Nothing (0 log noise) |
| Needs Error Object & Monitoring | --type=monitored |
Safe message + error_id tracking |
Full context, masked secrets, $previous root cause, alert |
๐ Documentation
For detailed usage and advanced configuration, please refer to the specific documentation chapters:
Core Architecture And Api
- ๐๏ธ Core Architecture & API Reference
- ๐ต๏ธโโ๏ธ How Does It Know the Request Context?
- ๐ The Complete Lifecycle: Generating, Throwing & Logging
Usage And Attributes
- ๐ Usage: The Attributes API
- ๐ก The Ultimate Exception Example
- ๐ค Do I Still Need
try/catch?
Renderers
- ๐จ Context Detectors & Renderers
- ๐ Web Renderer Example
- ๐ก API Renderer Example
- โก Livewire Renderer Example
- ๐ก๏ธ Filament Renderer Example
- โ๏ธ Inertia.js Renderer Example
Reporters And Integrations
- ๐ข Reporters
- ๐ฌ Slack Integration Example
- ๐ฆ NightWatch Integration Example
- ๐ฅ Flare (or Sentry) Integration
- ๐ Laravel Debugbar Integration Example
- ๐ Xdebug IDE Enrichment
- ๐ Routing to Different Log Channels
- ๐ Environment-Specific Reporting
- ๐๏ธ Immutable Compliance Audit Trail (
#[Audit])
Exception Handling Mechanics
- ๐ง Bypassing the Pipeline: Native Laravel Exceptions
- ๐ฆ The "Pass Through" Exceptions (Complete List)
- ๐คทโโ๏ธ Handling Generic (Un-decorated) Exceptions
- ๐ A Note on API Form Requests (
ValidationException) - ๐ก๏ธ Working with Gates & Permissions (
AuthorizationException) - ๐ Translated Error Messages Example
Advanced Configuration And Patterns
- โ๏ธ Advanced Configuration & Mechanics
- ๐งฉ Custom Detectors, Renderers, and Reporters
- ๐๏ธ Building a Custom Detector (Step-by-Step)
- ๐ Anti-Spam: Error Rate Limiting (In-Depth)
- โก Performance & Error Cache (Octane / Testing)
- ๐คฏ Exotic Use Cases & Advanced Patterns
- ๐ Advanced API: Complying with JSON:API Specification
Ecosystem And Commands
- โ๏ธ Queue Jobs Integration
- ๐ป Artisan Commands Integration
- ๐ ๏ธ Generating Exceptions:
make:error - ๐๏ธ Domain Driven Design (DDD) Support
- โก Caching & Optimization:
errors:cache&errors:clear - ๐ฉบ System Health Diagnostics:
errors:doctor - ๐ Exception Catalog:
errors:list
Testing And Ci
- ๐งช Testing Your Application
- ๐ฌ Static Analysis with Larastan
- ๐ค Continuous Integration (GitHub Actions)
๐งช Testing
The package includes a comprehensive test suite (built with Pest and Orchestra Testbench).
composer test
Run static analysis with Larastan:
composer phpstan
๐ License
The MIT License (MIT). Please see License File for more information.