Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Error Handler Laravel Package

symfony/error-handler

The Symfony ErrorHandler component provides tools to manage PHP errors and ease debugging. Register the error handler and debug class loader, render friendly error pages, and wrap risky code in ErrorHandler::call() to turn failures into exceptions.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. symfony/error-handler is a foundation-layer library that manipulates PHP's global error machinery (set_error_handler, set_exception_handler, register_shutdown_function, error_reporting). It has no service provider, no config file, and no container bindings — integration is via static calls (Debug::enable(), ErrorHandler::call(), FlattenException::createFromThrowable()). That procedural surface is a philosophical mismatch with Laravel's container-first conventions, but a practical fit for two specific jobs: converting silenced PHP warnings into throwables at call sites, and normalizing throwables into a serializable structure for logging/transport.

Critically, this package is almost certainly already in the vendor tree — laravel/framework lists symfony/error-handler as a direct requirement (alongside symfony/console, symfony/http-foundation, symfony/http-kernel). The decision is therefore not "add a dependency" but "use APIs we already ship." That materially lowers the risk profile and should be the first thing we verify (composer why symfony/error-handler).

Integration feasibility. High for narrow uses, low for broad ones.

  • Feasible and low-risk: ErrorHandler::call() wrapping individual risky operations; FlattenException for structured log payloads; DebugClassLoader in CI only; static error-page generation via the dump-static-error-pages command (added in 7.3) for the total-outage case Laravel cannot render.
  • Feasible but conflict-prone: anything that calls Debug::enable() or ErrorHandler::register(). Laravel's Illuminate\Foundation\Bootstrap\HandleExceptions already owns the error/exception/shutdown handlers and sets error_reporting(-1). Registering Symfony's handler afterwards replaces Laravel's, which disables App\Exceptions\Handler reporting/rendering, Spatie Ignition's debug pages, dontReport, and Laravel's log context enrichment. Two handlers cannot co-own the same globals.

Technical risk. Concentrated, not diffuse:

  • Handler hijack in production — the highest-severity failure. Debug::enable() in a live path leaks stack traces, query bindings, env-derived config, and request args via HtmlErrorRenderer. Must be treated as a hard guardrail, not a code-review convention.
  • Octane / long-lived workers — global handler registration performed in worker bootstrap persists across requests; a handler registered once in a Swoole/RoadRunner worker can bleed exception state between tenants. Verify behavior under Octane before any adoption beyond ErrorHandler::call().
  • Double reporting and alert fatigue — if Symfony's handler and Laravel's report() both fire, our Sentry/OpsGenie pipeline sees duplicates, inflating incident volume without adding signal.
  • Version-constraint collision — Symfony 8 requires PHP ≥ 8.4 and is only ~8 months of support per non-LTS branch; Symfony 7 requires PHP ≥ 8.2. Explicit composer require can conflict with laravel/framework's pin. Anchor to Laravel's constraint, never latest.
  • Data-quality caveat on the supplied metrics — 2,675 stars and "Dependents: 0" are artifacts of this being a read-only subtree split of symfony/symfony (issues and PRs live in the monorepo). Real-world adoption is effectively "every Laravel and Symfony application on the planet." The 46.4 composite score and 61.5 opportunity score understate maturity; do not use them as a comparison signal against standalone Laravel packages.

Key questions.

  1. Which exact symfony/error-handler version does our laravel/framework pin resolve to, and does it include dump-static-error-pages (≥7.3)?
  2. Are we willing to own a second, partially overlapping error pipeline, or is this strictly scoped to call-site utilities plus static pages?
  3. Who is the named owner of the global exception-handler chain, and what prevents a future contributor from adding Debug::enable() to AppServiceProvider?
  4. Do we need static 500/503 pages served by nginx for the "PHP is dead" case that Laravel structurally cannot handle?
  5. Is CI-level deprecation gating (DebugClassLoader) worth the noise budget, and who triages it?

Integration Approach

Stack fit. Laravel 10/11+ on PHP 8.2–8.4 is the sweet spot. Adopt the component's utilities, not its lifecycle. Concretely, three integration seams:

  1. Call-site hardening — ErrorHandler::call() around payment, file I/O, third-party SDK, and queue-job boundaries where PHP warnings are currently silenced by @ and silently degrade behavior. This is per-call, scoped, and reversible.
  2. Structured exception transport — FlattenException::createFromThrowable() inside a custom log processor or the report() path, producing a stable serialized shape (class, message, code, status, trace summary) for Sentry/Bugsnag/OTel. Replaces ad-hoc $e->getTraceAsString() payloads.
  3. Static error pages — generate branded 500/503 HTML via the dump command, ship to nginx error_page, and use HtmlErrorRenderer::setTemplate() for a custom generic template. This is the only realistic win for full-stack-down scenarios.

Migration path. Non-breaking and incremental, because nothing is being removed. Phase 0: confirm version and current handler ownership (composer why, inspect bootstrap/app.php withExceptions). Phase 1: DebugClassLoader wired into a CI job only, no runtime footprint. Phase 2: convert 5–10 highest-value @-suppressed call sites to ErrorHandler::call() behind feature-flagged jobs; measure retry/fallback rates. Phase 3: introduce FlattenException in the reporting path with duplicate-suppression enabled. Phase 4: static error pages. Only then evaluate whether further adoption is warranted.

Compatibility. MIT-licensed, no transitive surprises (it pulls psr/log and symfony/var-dumper, both already present). Symfony maintains 6.4 LTS, 7.x, and 8.x concurrently with a semiannual minor cadence, so patch drift is continuous but semver-safe. The release notes show frequent PHP-version-coupling fixes — E_DEPRECATED silencing behavior (7.3.1/7.2.8/6.4.23), PHP 8.5 fatal-error backtraces, PHP 8.6 error-message format changes — which is both reassuring (actively maintained) and a reminder that this component tracks PHP internals closely.

Sequencing. Utilities before renderers; renderers before global handlers, and global handlers not at all. Any proposal to ErrorHandler::register() in the request lifecycle should require an ADR and must first resolve the conflict with HandleExceptions — including a documented decision about whether Ignition is retired, which is a much larger product decision than "add a library."

Operational Impact

Maintenance. Near-zero incremental cost: the package is already a transitive dependency, patched by routine composer update alongside the framework. The real maintenance burden is our code — each ErrorHandler::call() call site is a small piece of custom control flow that must be understood by whoever reads the stack trace at 3 a.m. Budget code-review attention, not dependency management. The subtree-split repo has no independent issue tracker, so upstream bug reports go to symfony/symfony; we have no leverage on prioritization and should not plan around upstream fixes for our edge cases.

Support. Excellent, with a caveat: Symfony's support commitments are for Symfony, not for Laravel's integration of Symfony. If we bypass Laravel's exception handler, we are on an unsupported path as far as the Laravel ecosystem is concerned — Ignition, Telescope, Pulse, and Horizon all assume HandleExceptions is authoritative. Support for narrow utility usage is de facto community-standard; support for handler replacement is effectively self-support.

Scaling. Negligible per-request overhead when unused — ErrorHandler::call() installs and tears down a scoped handler per invocation, so cost is bounded by call frequency (avoid putting it inside hot loops). FlattenException with full arguments can be memory-heavy on deep traces; the 8.1.8 fix removing the backtrace retained by FatalError is relevant here. The component reserves memory specifically to render fatal errors after memory_limit exhaustion — real value, since Laravel's handler often cannot render under the same condition. Static pages offload error rendering entirely from PHP to nginx, which is the only approach that scales to zero PHP capacity.

Failure modes. Ranked by severity:

  1. Debug enabled in production → credential and source disclosure. Mitigation: APP_DEBUG-gated, plus a CI grep for Debug::enable and ErrorHandler::register outside tests/ and artisan scripts.
  2. Silent handler displacement → errors stop reaching Sentry; nobody notices until an incident review finds a three-week observability gap. Mitigation: assert handler identity in a smoke test post-deploy.
  3. Recursive logging — a logger that throws inside the handler throws again (addressed upstream in 8.1.8, but only on patched versions). Mitigation: version floor plus a circuit breaker in the reporter.
  4. Octane cross-request leakage of handler state. Mitigation: integration test under Octane before expanding scope.
  5. Duplicate alerts from parallel reporting paths → desensitized on-call rotation. Mitigation: single-writer rule for report().

Ramp-up. Low for the utility APIs — an engineer can adopt ErrorHandler::call() and FlattenException with the README and a code-review checklist. Moderate for the renderers and static-page generation, which require someone to own the nginx/error-page config and the branded template. High, and not recommended, for the global-handler path: it requires understanding Laravel's bootstrap sequence, the HandleExceptions contract, Octane's worker lifecycle, and the deprecation-detection internals of DebugClassLoader. Estimate: half a day to a working FlattenException integration, one to two weeks for a defensible static-error-page pipeline including ops sign-off, and an open-ended research spike for handler replacement that I would not fund.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core