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

Laravel Discord Error Tracker Laravel Package

tomatophp/laravel-discord-error-tracker

A Laravel package that sends application errors and exceptions straight to Discord via webhooks, so your team gets instant alerts with useful context instead of digging through logs. Quick to install and configure.

View on GitHub
Deep Wiki
Context7

Getting Started

This package reports Laravel exceptions to a Discord webhook. v2.0.0 is a modernization release: PHP 8.2+, Laravel 12/13, automatic exception reporting via the service provider, and hard guarantees around webhook failures.

Requirements first — if you're on Laravel 10 or 11, you stay on the 1.x line (v1 branch). Do not attempt to force v2 onto an older framework; the reportable-callback registration and the Illuminate contracts it relies on are not there.

composer require <vendor>/<package>:^2.0
php artisan vendor:publish --tag="discord-config"   # check the actual tag in the README

Minimum .env for a production app:

DISCORD_ERROR_WEBHOOK=https://discord.com/api/webhooks/...
DISCORD_ERROR_AUTO_REPORT=true

Where to look first, in this order:

  1. config/discord.php (published) — webhook URL, auto_report, mention, trace limit, timeout. Every v2 feature in the release notes has a key here.
  2. The service provider — it registers the reportable callback. Read it once so you understand why you no longer touch bootstrap/app.php.
  3. The README's upgrade section — the 1.x → 2.x diff is the highest-risk part of this release for existing installs.

First use case, end to end: ship to a staging environment, hit a route that throws, and confirm the embed arrives with exception class, message, and a truncated stack trace. Then immediately write the test — Http::fake() and assert the outbound request, so CI never depends on a live webhook.

Upgrading from 1.x: the single most important step is deleting your old handler wiring. v2 registers a reportable callback in the provider, so any leftover DiscordHandler (or equivalent) in your exception configuration will double-report every exception. Remove it, set DISCORD_ERROR_AUTO_REPORT=true, then verify you get exactly one message per exception.

Implementation Patterns

Let the framework do the reporting. Because v2 hooks Laravel's reportable callback, anything that flows through the exception handler is covered — HTTP requests, queued jobs (including failed-job reporting), scheduled commands, and manual report($e) calls. You generally do not call the package directly; you call report() and the package listens.

try {
    $this->chargeCustomer($order);
} catch (PaymentGatewayException $e) {
    report($e);          // reaches Discord if auto-report is on
    $this->markForRetry($order);
}

Gate by environment, always. Keep DISCORD_ERROR_AUTO_REPORT=false in .env.testing and .env.local, and use a distinct webhook per environment so a staging flood never pages on-call. A common shape:

# production
DISCORD_ERROR_WEBHOOK=https://discord.com/api/webhooks/AAA/prod
DISCORD_MENTION=<@&1234567890>
DISCORD_TRACE_LIMIT=15
DISCORD_TIMEOUT=2

# staging
DISCORD_ERROR_WEBHOOK=https://discord.com/api/webhooks/BBB/staging
DISCORD_MENTION=
DISCORD_TRACE_LIMIT=5

Filter before the webhook, not after. The cheapest way to keep the channel useful is to use Laravel's native reporting controls rather than package-specific rules:

// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions) {
    $exceptions->dontReport([
        \App\Exceptions\OrderValidationException::class,
        \Illuminate\Auth\AuthenticationException::class,
    ]);
})

Anything you dontReport never reaches the webhook, which also means it never burns your Discord rate limit.

Tune trace limit against your debugging style. Stack traces are the noisiest part of an embed. DISCORD_TRACE_LIMIT=5 gives you the throw site and the top of the call chain; 15 gives you enough to trace framework internals. Higher values are truncated anyway once the embed limit is hit, so there's no point setting 50 and hoping.

Keep the timeout aggressive. Reporting happens inline in the failure path. A 2-second timeout is generous; 10 seconds means a dead Discord connection adds 10s to every 500 response and every failing job. If you have high error volume or strict latency budgets, wrap reporting in a queued job of your own and report from there — the exception handler still runs, but the HTTP call moves off the request path.

Mentions are an on-call tool, not decoration. Set DISCORD_MENTION to a role ID (<@&ROLE_ID>) for production only, leave it empty in staging. This is the difference between "someone notices at 3am" and "everyone mutes the channel in a week."

Test it like any other outbound integration.

it('reports unhandled exceptions to discord', function () {
    Http::fake();

    $this->withoutExceptionHandling();

    expect(fn () => $this->get('/boom'))->toThrow(RuntimeException::class);

    Http::assertSent(fn ($request) => str_contains($request->url(), 'discord.com/api/webhooks')
        && $request['embeds'][0]['title'] === 'RuntimeException');
});

The withoutExceptionHandling() call matters — Laravel's test harness otherwise converts the exception into a response and the handler path may behave differently than you expect.

Deduplicate at the application layer. Discord webhooks are rate-limited per channel (roughly a handful of requests per few seconds). A retry storm or a hot loop will produce 429s, and v2 deliberately swallows them. Add a RateLimiter or a short-lived cache key around the reporting call site so one broken query doesn't generate 500 embeds.

Gotchas and Tips

Double reporting after the upgrade. The most common 1.x → 2.x mistake. The service provider now registers the reportable callback; if your old bootstrap/app.php or app/Exceptions/Handler.php still wires the package's handler, each exception goes out twice. Search your codebase for the package namespace and delete every manual registration.

"A failing webhook never breaks the app" hides real problems. The package correctly swallows network errors and never reports its own failure back to Discord — which means a misconfigured or rotated webhook URL fails silently. Add a health check or a php artisan tinker smoke test after any credential rotation, and keep your normal LOG channel as the source of truth. Discord is a notification side-channel, not your audit log.

Truncation eats the bottom of stack traces. Embeds have hard limits (description ~4096 chars, message content 2000). v2 truncates gracefully, but if you're chasing a deep framework trace, the frames you need may be the ones cut off. When debugging a specific incident, temporarily raise DISCORD_TRACE_LIMIT and check your storage/logs/laravel.log for the full trace — the log is always complete.

Config caching on deploy. DISCORD_* values are read through config. On Octane, Vapor, or any config-cached deploy, a changed env var does nothing until php artisan config:clear (or a fresh build). If "the webhook stopped working after the deploy," this is the first thing to check.

Mentions can mass-ping if you're careless. A raw @everyone in DISCORD_MENTION, or interpolating user-supplied exception messages into the payload, is how you accidentally notify an entire server. Only ever set mention values yourself, from config.

Webhook failure is not the same as delivery. Discord returns a 204 on accept; the message can still be dropped later. Don't build alerting logic that depends on Discord as a durable queue — for anything you must not miss, write to a log/DB and use Discord as the fast signal.

Branch pinning matters now that 1.x lives on v1. If you're supporting two apps on different Laravel versions in the same monorepo, use ^2.0 for Laravel 12/13 apps and dev-v1 (or the last 1.x tag) for the older one. Don't use a floating * constraint — you'll get an unresolvable dependency conflict the moment the app is on Laravel 11.

Extension points before forking. The published config is meant to be the customization surface — mention format, trace limit, timeout, and auto-report all live there. If you need a custom embed shape, check the README for a formatter/notifier contract to bind in a service provider; editing vendor files means the next composer update erases your work.

Debugging checklist for "no message arrived": DISCORD_ERROR_AUTO_REPORT actually true → config cache cleared → exception type isn't in dontReport → webhook URL responds to a manual curl -X POST → the exception is genuinely reaching Laravel's handler (not caught and swallowed upstream). That sequence resolves nearly every report.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core