Skip to main content

Exception Handling

MediatorK provides two layers of exception handling:

  1. ErrorTrackingPipelineBehavior — intercept every unhandled exception in the pipeline and forward it to a callback (e.g. crash reporting) before rethrowing.
  2. AggregateException — thrown by ContinueOnExceptionNotificationPublisher when multiple notification handlers fail.

ErrorTrackingPipelineBehavior

Register this behavior to wire crash-reporting services (Firebase Crashlytics, Sentry, Bugsnag, etc.) into the pipeline without touching handler code. The callback receives the original request and the throwable — the exception is always rethrown after the callback returns.

val mediator = MediatorFactory.create(
registrars = listOf(AppRegistrar()),
pipelineBehaviors = listOf(
ErrorTrackingPipelineBehavior(
order = Int.MAX_VALUE, // innermost by default — fires closest to the handler
onError = { request, error ->
Crashlytics.recordException(error)
},
),
),
)

Use order = Int.MAX_VALUE (the default) to place the tracker innermost — it captures every exception directly from the handler before it bubbles up through retry or timeout. If you only want to report failures after all retries are exhausted, use an order lower than RetryPipelineBehavior (e.g. Int.MIN_VALUE) to place it outside the retry wrapper.


Handling missing handlers

By default, send() for an unregistered request type throws MissingHandlerException. Customize this behavior via missingRequestHandler in MediatorFactory.create:

// Default — throws immediately
val mediator = MediatorFactory.create(
registrars = listOf(AppRegistrar()),
missingRequestHandler = ThrowMissingRequestHandler(), // default
)

// Silent — returns a default value instead of throwing
val mediator = MediatorFactory.create(
registrars = listOf(AppRegistrar()),
missingRequestHandler = SilentMissingRequestHandler(default = null),
)
danger

SilentMissingRequestHandler silently drops requests. Only use it when unhandled requests are intentional — misconfiguration will produce no error and no trace.


Built-in exceptions

ClassThrown when
MediatorExceptionBase class for all MediatorK errors
MissingHandlerExceptionsend() called for a request type with no registered handler
MissingStreamHandlerExceptionstream() called for a stream request type with no registered handler
MissingNotificationHandlerExceptionNotification published with no registered handlers (only when using ThrowMissingNotificationHandler)
AggregateExceptionContinueOnExceptionNotificationPublisher — one or more notification handlers failed

MissingHandlerException

// message includes the unresolved type AND all registered types to aid debugging:
// "No handler registered for 'DeleteUserCommand'. Registered: GetUserQuery, CreateOrderCommand"

AggregateException

try {
mediator.publish(SomeNotification())
} catch (e: AggregateException) {
e.message // "2 handler(s) failed: ..."
}

trySend — Result wrapper

Use trySend when you want to handle errors as Result instead of catching exceptions:

val result: Result<User> = mediator.trySend(GetUserQuery("user-1"))
result.onSuccess { user -> ... }
result.onFailure { error -> ... }

Next

Validation