emgiezet/errbit-php
PHP client for sending exceptions and errors to Errbit, the Airbrake-compatible error catcher. Integrates with Laravel to report exceptions automatically, helping you centralize error tracking and monitor application failures.
Install and wire it up
composer require emgiezet/errbit-php
The package exposes a single client class (the README's Client/Notifier example is the canonical reference — read it before anything else, the API surface is small). Minimum viable setup:
$notifier = new \Emgiezet\Errbit\Client(
'YOUR_PROJECT_API_KEY', // from the Errbit project's "API key"
'https://errbit.example.com' // no trailing slash
);
try {
riskyThing();
} catch (\Throwable $e) {
$notifier->notify($e);
throw $e;
}
Where to look first
/notifier_api/v2/notices), so anything you know about airbrake/airbrake-php transfers.composer.json for the require block — check the PHP constraint against your app before committing.First real use case
Don't sprinkle notify() calls around. Wire it once into your framework's central error reporter. In Laravel, that's app/Exceptions/Handler.php:
public function report(Throwable $e): void
{
if ($this->shouldReport($e) && app()->bound('errbit')) {
app('errbit')->notify($e);
}
parent::report($e);
}
That single hook gives you coverage of HTTP requests, queue jobs, and console commands for free.
Bind it as a singleton. The client holds config and a HTTP handle; you do not want a new one per throw. In a service provider:
$this->app->singleton('errbit', fn () => new \Emgiezet\Errbit\Client(
config('services.errbit.key'),
config('services.errbit.host'),
));
Then inject 'errbit' or type-hint wherever needed. Keep the key in config/services.php, not in code — and out of .env files you might commit.
Report vs. raise pattern. Inside catch blocks in a service, notify-and-rethrow. Never swallow after notifying:
try {
$this->gateway->charge($order);
} catch (GatewayException $e) {
$this->errbit->notify($e);
throw $e; // let the framework decide the HTTP response
}
Exception grouping. Errbit groups by exception class + message + backtrace (the "fingerprint"). If the same bug appears as fifty separate groups, your message templates are interpolating IDs. Strip volatile data from the message before it ever reaches the notifier:
$e = new \RuntimeException("Payment failed for order {$order->id}"); // ❌ new group per order
$e = new \RuntimeException('Payment failed'); // ✅ one group
$notifier->notify($e); // attach $order->id as context, not as message
Context enrichment. Send user/session/params alongside the exception — AirBrake v2 supports this via the params/environment sections. At minimum attach the authenticated user ID and the request ID so you can correlate with logs:
$notifier->notify($e); // plus whatever context setter this package exposes;
// if it doesn't, add a thin wrapper class that composes the payload yourself
Queue/worker integration. Wrap the job handler, not the whole worker loop, or a single poisoned job will report repeatedly. In Laravel, report() inside failed() is usually enough.
Middleware for API surfaces. A terminal middleware that catches, notifies only 5xx, and lets the handler format the response keeps 4xx validation noise out of Errbit.
Graceful degradation. Wrap the notifier itself in a try/catch and log-and-continue. Monitoring must never take down the app:
try { $this->errbit->notify($e); } catch (\Throwable) { /* never throw from a reporter */ }
Errbit is effectively unmaintained. The project has been in low-maintenance mode for years. This package still speaks the same HTTP protocol, which is why it keeps working, but plan a migration path to Sentry/Bugsnag if your error volume matters. Treat the 44 stars and the "only one working" tagline as a signal: this is a small library, not a maintained SDK.
Timeouts will bite you. A synchronous curl POST on every exception means a slow or unreachable Errbit blocks the request thread. Set a short timeout (2–3s) on the underlying transport, and if you can't configure it through this package, subclass the client or wrap it in your own with a lower CURLOPT_TIMEOUT. For high-traffic apps, push notifications onto a queue instead of calling notify() inline.
Fatal errors still aren't automatic. Registering the notifier in Handler::report() covers exceptions, but not PHP fatals or memory exhaustion. Add a register_shutdown_function that inspects error_get_last() if those matter to you — most teams don't bother.
Sensitive data leakage. AirBrake v2 payloads can include request params, headers, and session data. Errbit is self-hosted so the risk is internal, but credentials in headers, Authorization tokens, and password fields get stored in plaintext. Sanitize before sending; configure Errbit's own filters as a second layer.
API key ≠ auth token. Errbit's X-Airbrake-Project-Key header (or the project_key param) is per-project. Mixing keys across environments is the #1 cause of "errors not appearing." Verify by hitting the endpoint manually:
curl -i -X POST https://errbit.example.com/notifier_api/v2/notices \
-H 'Content-Type: text/xml' \
-H 'X-Airbrake-Project-Key: YOUR_KEY' \
--data-binary @notice.xml
A 200 with an empty body means accepted; 40x means bad key or wrong path.
Environment name matters. Set environment (production/staging/local) explicitly, otherwise staging exceptions will page you. If the package doesn't expose it in the constructor, it may read from APPLICATION_ENV — check the source.
Self-signed certificates. Self-hosted Errbit often sits behind internal TLS. Either import the CA into your PHP curl trust store or you'll silently get curl error 60. Don't disable verification as the fix.
Version pinning. The 2026-08-18 release date is recent, but with a small package it's worth pinning to an exact version in composer.json and testing on upgrade — there's no formal semver guarantee with a single-maintainer project.
Don't over-notify. Reporting every caught-and-recovered exception is noise that trains your team to ignore Errbit. Report only what shouldReport() says, and consider a per-request "already reported" guard so a bubbling exception isn't sent three times up the stack.
How can I help you explore Laravel packages today?