Open-source project
GrahamCampbell/Laravel-Exceptions avatar
GrahamCampbell/Laravel-Exceptions

Laravel Exceptions swaps the framework's error page for a pipeline you configure

Provides a powerful error response system for Laravel

588 stars52 forksPHPMIT

At a glance

What is it?
Laravel Exceptions gives a Laravel application a configurable error response system for both development and production, built from ordered transformers, priority-sorted displayers and per-exception filters. It optionally uses Whoops for the debug pages, and the documentation is explicit that there is no usage guide.
Who is it for?
Laravel Exceptions suits a Laravel application that needs different error output in development and in production, particularly one that already serves JSON to some clients and HTML to others, because the displayer priority and filter mechanism is what makes that possible without branching in your own handler.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 170 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two required changes after composer require, one of them a handler swap

Laravel Exceptions was created by, and is maintained by, Graham Campbell, and provides a powerful error response system for both development and production for Laravel. The package name on Packagist is graham-campbell/exceptions, and the current version line is 17.2.

Installation is one Composer command:

bash
$ composer require "graham-campbell/exceptions:^17.2"

After that there are two follow-up steps, and one of them is not optional. If you are not using automatic package discovery, you need to register the `GrahamCampbell\Exceptions\ExceptionsServiceProvider` service provider in your `config/app.php`. Then, in the documentation's words, you MUST change your `App\Exceptions\Handler` to extend `GrahamCampbell\Exceptions\ExceptionHandler`.

That second step is the real integration point. The package does not sit beside your exception handler, it replaces the base class of it. Everything the framework's default handler does, from choosing a status code to deciding whether to report the exception, now flows through the package's handler instead.

The version requirements are stated plainly: this version requires PHP 7.4 to 8.4 and supports Laravel 8 to 11. A compatibility table in the documentation maps each release of the package against each Laravel minor version, and the pattern is a moving window. Version 11.3 of the package covers Laravel 5.5 to 5.8. Version 14.1 covers 7 and 8. Version 16.0 covers 8 and 9. Version 17.2 covers 8, 9, 10 and 11.

So a Laravel 8 application can use the current release, and a Laravel 11 application can too, but an application on Laravel 10 that wants the newest features of the package would be pinned to a version whose support window is narrower. That table is the thing to check first, before anything else in the documentation.

The displayer pipeline, and why ordering is the whole design

The configuration file is where this package differs from everything else in the Laravel error handling world, and the documentation describes five options that together form a pipeline.

The transformers option defines each of the exception transformers set up for your application, and it allows you to turn your exceptions into other exceptions, such as turning a domain exception into a different type for perfect results when passed to the displayers. The list is processed in order, and subsequent transformers can still modify the results of previous ones if required.

The displayers option defines the displayers, and they are sorted by priority. The rule for selection is the interesting part. When you are in debug mode, the first valid displayer from the list is selected. When you are not in debug mode, all verbose displayers are filtered out and then the first valid displayer from the new list is selected.

That single sentence is the package's whole reason to exist. A displayer can declare itself verbose, which means it is only eligible in debug mode, so the same configuration yields a Whoops debug page in development and a clean production page in production, with no environment check in your own code.

The filters option applies filters to the displayers, which is how you work out which displayer to use for each exception. The documentation says this includes things like content type negotiation, which is how a JSON API client gets a JSON error body while a browser gets HTML. The default option names the displayer to use if the filters have removed every candidate, which is the fallback that prevents an exception from having no output at all.

The levels option defines the log levels for each exception, where an exception that passes the test for a key uses the log level associated with that key. So the severity of a log line is a function of the exception type, configured in one array.

Whoops is optional, and detected rather than configured

The debug error pages come from Whoops, and the package treats it as an optional extra rather than a dependency.

If you want the debug error pages available, you require Whoops yourself as a dev dependency:

bash
$ composer require "filp/whoops:^2.15" --dev

The documentation then says the debug displayer will automatically detect the presence of Whoops, and invites you to read the source to understand how it works. Automatic detection is a nice property for a package that has to work with and without an optional component, and it also means there is no config flag to forget.

The dependency direction is worth noting. The Whoops requirement is scoped with the --dev flag, which keeps it out of your production dependency tree. That is a sensible default for a debugging aid, and it also means the displayer that depends on it is one of the verbose ones, filtered out in production anyway. The two decisions line up.

This package makes the choice of what an error looks like, and to whom, a configuration array rather than a framework default.

Publishing the config, and reading it between releases

Configuration is optional, but the config file is how you make the package do anything beyond the default.

You publish all vendor assets with:

bash
$ php artisan vendor:publish

That creates a `config/exceptions.php` file in your app that you can modify. The documentation adds a warning that is easy to skim: make sure you check for changes to the original config file in this package between releases.

That warning is the operational cost of the whole approach. Because the configuration lives in your application rather than in the package, and because the package's defaults change between releases, a version bump can introduce new config keys or change the meaning of existing ones, and the only way to find out is to diff the package's shipped config against yours. Anyone running a monitoring or alerting stack that depends on the shape of this file should treat that diff as part of the upgrade, not after it.

The repository layout is a standard Composer package: src/ for the implementation, config/ for the shipped configuration, resources/ for the assets the displayers need, tests/ with a phpunit.xml.dist, and composer.json at the root. The documentation also links a change log, a releases page, a security policy, a licence file, a code of conduct and contribution guidelines, which is a well-governed repository by the usual standards.

No usage documentation, and a security contact that is not the author

The usage section is three lines long and says so outright: there is currently no usage documentation for Laravel Exceptions, but the project is open to pull requests for it.

This is the weakest part of the package and it is not a minor gap. The config options are described in the configuration section, so you can learn what transformers, displayers, filters, default and levels mean. What you cannot learn from the documentation is what happens in practice, what a good set of transformers looks like, or how the pieces behave in an application that already has a custom exception handler. The documentation itself points you to the source code for the Whoops detection mechanism, which is an implicit admission that the code is the reference here.

If you are evaluating this package, that is the line to read: a well-described configuration surface with no worked examples of how people actually use it.

The security reporting route is also unusual and worth noting. If you discover a security vulnerability within the package, the instruction is to send an email to [email protected]. That is Tidelift's security address, not the author's, which is the standard arrangement for a package funded through Tidelift's open source security programme. It means the reporting path is handled by a third party rather than by the maintainer directly.

The licence is MIT, which is the straightforward case for a package like this, and there is a licence file in the repository root alongside a CHANGELOG.md and the github community files.

Release cadence, and the branch name that explains the versioning

The default branch is 17.2, which is an unusual choice and worth reading correctly. It is not a maintenance branch, it is the branch for the current minor version, and the release names follow the same pattern, with v17.2.0 labelled V17.2 and dated 02/03/2025.

The release history is short at the top. v17.2.0 was released on 2025-03-02, v17.1.0 on 2023-12-04, and v17.0.0 on 2023-03-07. So the 17.x line has three releases across two years, and the last push to the repository was on 2026-05-06.

That gap between the last release and the last push is about fourteen months, which is a long time for a package that is still receiving commits. The project is not archived and the version line is still moving, so it is not dead, but it is also not the kind of dependency where you assume a new release will arrive when a Laravel version does.

For an adopter, the practical consequence is that the compatibility table in the documentation is the only forward-looking information you get. If your application is on Laravel 10 or 11, version 17.2 supports you, but there is no signal in the repository about which Laravel release the next version will target. Anyone planning to stay current with Laravel major versions is betting on the maintainer continuing to update the compatibility table, and there is no commitment in the documentation to that.

That is not a reason to avoid the package, but it is a reason to pin the version and to check the table before each upgrade, which the documentation already tells you to do for the config file.

Editorial conclusion

Laravel Exceptions suits a Laravel application that needs different error output in development and in production, particularly one that already serves JSON to some clients and HTML to others, because the displayer priority and filter mechanism is what makes that possible without branching in your own handler. It is the wrong choice if you want a finished error page with no configuration, since the package ships a pipeline and expects you to fill it in, and it is the wrong choice if you are on a Laravel version outside 8 to 11. Before adopting it, publish the vendor assets and read config/exceptions.php, and confirm your app's exception handler extends GrahamCampbell\Exceptions\ExceptionHandler, because that change is mandatory rather than optional.

Frequently asked questions

How do I install Laravel Exceptions and what must I change afterwards?

Run composer require "graham-campbell/exceptions:^17.2". If you are not using automatic package discovery you must register the GrahamCampbell\Exceptions\ExceptionsServiceProvider in config/app.php, and you MUST change your App\Exceptions\Handler to extend GrahamCampbell\Exceptions\ExceptionHandler. The current version requires PHP 7.4-8.4 and supports Laravel 8-11.

How does Laravel Exceptions decide which error page to show in production?

The displayers option defines the displayers and they are sorted by priority. In debug mode the first valid displayer from the list is selected, and when not in debug mode all verbose displayers are filtered out and then the first valid displayer from the remaining list is used. Filters narrow the choice further, for example by content type negotiation, and the default option names the displayer to fall back on if the filters remove every candidate.

Do I need Whoops to use Laravel Exceptions?

Only for the debug error pages. The documentation says that if you want the debug error pages available you need to require filp/whoops:^2.15 as a dev dependency, and that the debug displayer will automatically detect the presence of Whoops. The package itself does not require it.

Official sources

  1. GrahamCampbell/Laravel-Exceptions on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/grahamcampbell-laravel-exceptions.svg)](https://hysenlabs.com/projects/grahamcampbell-laravel-exceptions)

Was this useful?