Skip to main content

Processor-Failure Handling

A SystemEventProcessorInterface::processEvent() call can fail — a disk full, a downstream service unreachable, a malformed custom placeholder. The System Events package makes sure that failure never propagates into the code that fired the original, unrelated event.


How Isolation Works

Both the live wildcard listener and buffered-event replay (replayInMemoryEvents()) catch Throwable around every individual processEvent() call:

  • If a processor throws while handling one event, that event's failure is reported (see below) and processing continues with the next event.
  • The application code that originally fired the event never sees the exception and is never interrupted by it.

This isolation lives in SystemEventsServiceProvider, not in the processor contract itself — a SystemEventProcessorInterface implementation (including FileSystemEventProcessor and CompositeSystemEventProcessor) is still free to throw; it's the provider's job to catch it.


Default Behavior

If you don't configure anything, a processor failure is reported via PHP's error_log():

[system-events] Failed to process event "payment.charged": Unable to write to log file: /var/logs/system-events.log

Custom Failure Hook

To handle failures yourself — increment a metric, forward to a monitoring service, retry against a fallback destination — pass a closure to the onProcessingFailure constructor argument of SystemEventsServiceProvider:

use DomainFlow\SystemEvents\Provider\SystemEventsServiceProvider;
use Throwable;

$app->registerProvider(new SystemEventsServiceProvider(
onProcessingFailure: function (Throwable $e, string $eventName): void {
// e.g. increment a metric, forward to a monitoring service
Metrics::increment('system_events.processing_failed', ['event' => $eventName]);
}
));
Closure ParameterTypeDescription
$eThrowableThe exception thrown by processEvent().
$eventNamestringThe name of the event that failed to process.

💡 When onProcessingFailure is provided, it replaces the default error_log() behavior entirely — it is not called in addition to it.


Interaction with Fan-Out Processing

CompositeSystemEventProcessor aggregates every destination's failure into a single CompositeProcessingException and throws it once all configured processors have run. That exception is caught by this same isolation mechanism like any other processEvent() failure — see Fan-Out Processing for details on inspecting the individual failures it wraps.