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

X Error Laravel Package

vkm-apps/x-error

A Laravel package for richer error handling, offering clearer exception reporting and more helpful error responses. It integrates with Laravel's exception handler to make debugging and production errors easier to diagnose and resolve.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Read the source before you install it. There is no reliable spec here. The description advertises "consistent JSON error responses" and "pluggable handlers," but every release note talks about views and languages. Open src/ on GitHub and answer three questions in five minutes:

  • Does XErrorServiceProvider::boot() only call publishes() and loadTranslationsFrom() (safe), or does it call loadViewsFrom() / touch Handler::registerErrorViewPaths() (it now owns your error path)?
  • What does config/x-error.php actually contain — a locale list, a fallback, a resolution strategy, or nothing?
  • What are the illuminate/support and php constraints in composer.json, and is there a tagged semver release you can pin exactly?

2. Install and publish everything, in one shot.

composer require vkm-apps/x-error
php artisan vendor:publish --provider="VkmApps\XError\XErrorServiceProvider"

If the provider advertises granular tags, publish them explicitly (tag names vary — check boot()): typically x-error-config, x-error-views, x-error-lang. Then php artisan config:cache if you run cached config in that environment.

3. Look first at what landed. You should now have:

  • config/x-error.php — the only real API surface
  • resources/views/vendor/x-error/ (or resources/views/errors/) — the Blade views
  • lang/vendor/x-error/{locale}/ — the translation files
  • lang/{locale}/ if it publishes into the app's own namespace

Count the populated locales. Release v1.0.8 says "Added new languages" — verify which ones are actually translated and which are stubs that fall back to English.

4. First use case: prove it end-to-end, in a browser and in curl.

# Disable debug or you will see Ignition, not these views
APP_DEBUG=false

curl -i -H "Accept-Language: fr-FR,fr;q=0.9" https://app.test/definitely-missing
curl -i -H "Accept-Language: de-DE" https://app.test/nope
curl -i -H "Accept: application/json" https://app.test/nope   # must still be JSON

Check four things on each response: the status code (404), Content-Type, the rendered locale, and — for 429/503 — the Retry-After header. If the JSON call returns HTML, stop and evaluate whether the package is worth it for your app, because it will break SPA/API consumers.

5. Make the adoption decision early. Given zero stars, zero dependents, and a single maintainer on the failure path, the default plan should be publish-and-drop: composer require, publish, copy the views and lang files into your repo, remove the dependency, own the files. You are buying ~10 Blade views and a translation set, not a runtime dependency. If you do decide to keep it, pin an exact version and keep the provider behind a config flag.

Implementation Patterns

Pattern 1 — Publish-and-vendor (recommended). After publishing, move the views into resources/views/errors/ under Laravel's own resolution convention (404.blade.php, 403.blade.php, 419.blade.php, 429.blade.php, 500.blade.php, 503.blade.php) and move the translations into your normal lang/{locale}/ tree. Remove the package from composer.json. You now have a diffable, reviewable set of views that behaves exactly like the rest of your app, and a composer audit surface that can't regress.

composer require vkm-apps/x-error
php artisan vendor:publish --provider="VkmApps\XError\XErrorServiceProvider" --force
# inspect, then:
git add resources/views/errors lang config/x-error.php
composer remove vkm-apps/x-error

Pattern 2 — Pinned dependency with a kill switch. If the package does dynamic locale negotiation you'd rather not reimplement, keep it but wrap the provider registration behind an env flag (X_ERROR_ENABLED) in config/app.php / bootstrap/providers.php, and pin an exact version ("vkm-apps/x-error": "1.1.1", not ^1.1). That gives you a one-line rollback if a view starts throwing.

Keep the error views on a shared layout. Error pages are the moment users lose confidence, so they should look like the app, not like a different product. Extract the shipped views' markup into resources/views/errors/layout.blade.php and make every status extend it. Do this after you've confirmed the package's views render, not before.

Locale resolution is the part you'll rewrite. Whether locale comes from session, Accept-Language, a route segment, or per-tenant config, the resolution has to be self-contained. The pattern that survives real incidents:

// App\Exceptions\ResolveErrorLocale
public static function resolve(Request $request): string
{
    return once(function () use ($request) {
        try {
            if ($request->hasSession() && $request->session()->has('locale')) {
                return $request->session()->get('locale');
            }
        } catch (\Throwable) {
            // session store unavailable (419/500) — fall through
        }

        return $request->getPreferredLanguage(
            array_keys(config('x-error.locales', ['en']))
        ) ?? config('app.fallback_locale', 'en');
    });
}

Wire it from a small middleware or view composer, and make sure the fallback is guaranteed — never let locale resolution throw inside an error render.

Test every status explicitly. The package ships views, not tests. Write one feature test per status and assert all four dimensions:

it('renders a localized 404', function () {
    $this->withHeader('Accept-Language', 'fr-FR')
        ->get('/missing-page')
        ->assertNotFound()
        ->assertHeader('Content-Type', 'text/html; charset=UTF-8')
        ->assertSee('Page introuvable'); // whatever the fr copy actually says
});

it('keeps JSON for API clients', function () {
    $this->withHeader('Accept', 'application/json')
        ->get('/missing-page')
        ->assertNotFound()
        ->assertJsonStructure(['message']);
});

it('preserves Retry-After on 503', function () {
    // trigger maintenance mode or throw HttpException(503)
    $this->get('/')->assertStatus(503)->assertHeader('Retry-After');
});

CDN and cache correctness. Error responses must never be pinned to a wrong locale. Either send Cache-Control: no-store on all error responses, or send Vary: Accept-Language and cache per-locale. Add this to your exception handler or a middleware — don't rely on the views to handle it.

Content ownership per locale. The translation files are the durable asset. Put contact details, legal links, and support addresses in the lang files (or a config/x-error.php block keyed by locale), never hard-coded in Blade. That makes the recurring update — new GDPR line, changed phone number, new market — a one-file diff rather than a template edit.

Degraded mode as a design constraint. Write the 500 and 503 views to depend on nothing: no @auth, no DB::, no Cache::, no @vite, no ->tenant() calls. If you can render them with the app's database and cache switched off, they will render during a real outage.

Gotchas and Tips

APP_DEBUG=true hides the whole package. Locally you'll get Ignition/Whoops and wonder if the install worked. Test in an environment with debug off, or temporarily flip it — but don't ship a debug-on test as proof.

Error-page recursion is the #1 failure mode. A missing compiled view, an unresolvable lang key, a @vite directive against a missing manifest, or a locale resolver that hits a dead session store all produce a blank screen or an exception loop exactly when the user is already confused. Mitigation: render the views in a test with session/cache/database faked out, and keep the 500 view's dependency surface at zero.

The exception path doesn't run your locale middleware. Whatever sets app()->setLocale() on normal requests (a SetLocale middleware, Route::bind, session) is not guaranteed to run when the handler renders an error view. That's why locale must be resolved explicitly inside the error path — don't assume the package's default does what your app does.

Retry-After and 419 guidance are easy to lose. A translated 429 page is worthless if it drops Retry-After; a 419 page is worse than useless if it doesn't tell the user to refresh and re-submit the form. Verify these explicitly — they're behavioral contracts, not copy.

Marketing copy ≠ behavior. The description claims JSON negotiation and pluggable logging handlers; the releases only mention views and languages. Treat the README and the source as the only spec and test the JSON path yourself. If your app is API-first, Inertia-first, or Livewire-first, this package is likely irrelevant to most of your traffic.

Don't let error views leak exception context. Third-party error views are code you didn't write. Grep them for $exception, $message, getMessage(), stack output, or debug guards before shipping with APP_DEBUG=false. On a 500 with a database exception, that's a compliance exposure.

vendor:publish --force will silently overwrite your edits. If you stay on the dependency, publish once, commit, and never re-publish without diffing. This is the strongest argument for publish-and-drop: vendored files are protected by normal code review; vendor-published files are one careless command away from deletion.

Version and constraint checks before you commit. Confirm Laravel 13 support is real (v1.0.4 claims it) and not just a composer.json edit; run composer audit; check whether upstream is triaging issues. Zero dependents means nobody has validated these views against Sentry, Telescope, Route::fallback(), or a customized App\Exceptions\Handler — you'll be the first.

Route::fallback() wins or loses depending on ordering. A catch-all route short-circuits NotFoundHttpException, so your localized 404 never renders. Grep for Route::fallback before assuming the package is broken.

Config caching will bite you. After editing config/x-error.php, run php artisan config:cache in cached environments or your changes are invisible in production while working locally.

Keep the lang files overridable. If the package loads translations from its own namespace (lang/vendor/x-error/), confirm your copy in lang/{locale}/ takes precedence — otherwise a translator's update in the expected location silently does nothing. Test by changing one string and requesting the page.

Name an owner for the copy. These translations decay: phone numbers, legal references, and support hours change per market. Before merge, decide who owns lang/*/errors.php and when it gets reviewed. That's a staffing decision, not an engineering one, and it's the reason publish-and-vendor beats a dependency here.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator