Skip to main content

Exception catalog

Every typed exception class across all Nexus packages is listed here. Entries are grouped by package and sorted alphabetically within each group. The base class hierarchy is:

  • NexusException extends RuntimeException — checked runtime exceptions; declare in @throws.
  • NexusLogicException extends LogicException — programmer errors and invariant violations; not tracked by Psalm's checked exception analysis.
  • Package-specific bases (MailboxException, SerializationException, AuthException, HttpException) extend one of the two above.

nexus-core

ActorException

Monadial\Nexus\Core\Exception\ActorException

Abstract base for actor runtime exceptions. Extends NexusException. Catch this to handle any actor-layer failure without enumerating subclasses.

ActorInitializationException

Monadial\Nexus\Core\Exception\ActorInitializationException

When thrown: Behavior::setup() factory throws, or the actor cell fails during the New → Starting transition.

Cause: The behavior factory or onPreStart logic threw an exception before the actor was ready to process messages.

Recovery: Inspect $e->actor (the ActorPath) and $e->reason to identify which actor failed. Fix the initialization logic or catch the exception in the behavior factory and return a fallback behavior. The parent actor receives a ChildFailed signal.

See also: Lifecycle signals — ChildFailed

ActorNameExistsException

Monadial\Nexus\Core\Exception\ActorNameExistsException

Extends NexusLogicException.

When thrown: $ctx->spawn(Props, $name) is called when a child with $name already exists under the same parent.

Cause: Two spawn() calls use the same name string within the same parent actor, or a respawn is attempted while the previous child is still alive.

Recovery: Use $ctx->child($name) to check whether the child already exists before spawning. Alternatively, use $ctx->spawnAnonymous() to avoid naming collisions. Names must be unique per parent.

See also: Gotchas — child-name uniqueness

AskTimeoutException

Monadial\Nexus\Core\Exception\AskTimeoutException

Extends ActorException and implements FutureTimeoutException.

When thrown: The future returned by ActorRef::ask() is not resolved within the specified Duration.

Cause: The target actor did not reply within $timeout. The actor may be overloaded, stopped, or the handler did not call $ctx->reply().

Recovery: Check $e->target and $e->timeout. Increase the timeout if the actor is legitimately slow. If the actor may be dead, check $ref->isAlive() before calling ask(). Note that asking a dead actor produces a timeout, not an immediate exception.

See also: Gotchas — ask to dead actor

InvalidActorPathException

Monadial\Nexus\Core\Exception\InvalidActorPathException

Extends NexusLogicException.

When thrown: An actor path string fails validation (illegal characters, missing leading slash, or malformed segments).

Cause: A hard-coded or dynamically assembled path string does not conform to the path grammar akka://system-name/user/parent/child.

Recovery: Check $e->invalidPath. Actor names may only contain letters, digits, hyphens, and underscores. System names follow the same rules.

InvalidActorStateTransition

Monadial\Nexus\Core\Exception\InvalidActorStateTransition

Extends NexusLogicException.

When thrown: The actor cell receives a state-machine event that is illegal from its current state (e.g., a Suspend while already Stopped).

Cause: Internal invariant violation — usually triggered by sending internal system messages directly from user code, or by a runtime bug.

Recovery: Do not send Suspend, Resume, or Kill from user code. If you see this in production, file an issue — it indicates a runtime bug.

See also: System messages — Suspend/Resume

InvalidBehaviorException

Monadial\Nexus\Core\Exception\InvalidBehaviorException

Extends NexusLogicException.

When thrown: A behavior handler returns an invalid value (e.g., null).

Cause: Handler returned something other than a Behavior instance. Psalm level-1 catches this at analysis time; this exception is the runtime fallback.

Recovery: Return Behavior::same(), Behavior::stopped(), or a new behavior from every handler path. Never return null.

MaxRetriesExceededException

Monadial\Nexus\Core\Exception\MaxRetriesExceededException

Extends NexusException.

When thrown: A child actor has been restarted $maxRetries times within $window and still fails. The supervision strategy stops the child and propagates this exception to the parent.

Cause: Persistent failure — the child crashes on every restart, indicating a structural problem rather than a transient error.

Recovery: Inspect $e->child, $e->maxRetries, $e->window, and $e->lastFailure. Address the root cause. Consider using SupervisionStrategy::exponentialBackoff() to add delay between restart attempts.

See also: Config — exponentialBackoff

NexusException

Monadial\Nexus\Core\Exception\NexusException

Abstract base. Extends RuntimeException. All checked Nexus exceptions extend this. Catch NexusException to handle any typed Nexus failure in one place.

NexusLogicException

Monadial\Nexus\Core\Exception\NexusLogicException

Abstract base. Extends LogicException. All programmer-error exceptions extend this. These represent bugs and invariant violations; do not catch and swallow them.

NoSenderException

Monadial\Nexus\Core\Exception\NoSenderException

Extends ActorException.

When thrown: The handler calls $ctx->reply($msg) (or $ctx->sender()) on a message that was sent via tell() — not ask().

Cause: tell() does not set a sender or reply-to ref. Only ask() creates a reply channel.

Recovery: Guard with $ctx->sender()->isDefined() before calling reply(). If the handler is used for both fire-and-forget and request-reply messages, branch on the sender option.

See also: Gotchas — reply() asymmetry

StashOverflowException

Monadial\Nexus\Core\Exception\StashOverflowException

Extends NexusException.

When thrown: $ctx->stash() is called and the stash buffer has reached capacity.

Cause: The actor is stashing faster than it is unstashing. Default stash capacity is 100 messages.

Recovery: Check $e->capacity and $e->size. Either increase stash capacity by configuring Props::withMailbox() with a larger bounded mailbox, reduce the rate of stashing, or call $ctx->unstashAll() more aggressively.

See also: Gotchas — stash capacity


nexus-runtime

FutureCancelledException

Monadial\Nexus\Runtime\Exception\FutureCancelledException

Implements FutureException (extends RuntimeException).

When thrown: A Future is explicitly cancelled before it resolves.

Cause: The caller cancelled the future via cancel() or the runtime shut down with pending futures.

Recovery: Wrap ->await() in a try/catch for FutureCancelledException. If the future represents a cancelled ask, the reply was never sent.

FutureException

Monadial\Nexus\Runtime\Exception\FutureException

Marker interface. Extends Throwable. Implemented by FutureCancelledException and FutureTimeoutException (the latter also implemented by AskTimeoutException). Catch FutureException to handle any future failure.

FutureTimeoutException

Monadial\Nexus\Runtime\Exception\FutureTimeoutException

Marker interface. Extends FutureException. Implemented by AskTimeoutException. Represents a future that exceeded its deadline.

InvalidMailboxConfigException

Monadial\Nexus\Runtime\Exception\InvalidMailboxConfigException

Extends LogicException.

When thrown: A MailboxConfig is constructed with invalid parameters (e.g., a negative capacity).

Cause: Programmer error — passing an out-of-range value to MailboxConfig::bounded().

Recovery: Validate capacity values before construction. Capacity must be a positive integer.

MailboxClosedException

Monadial\Nexus\Runtime\Exception\MailboxClosedException

Extends MailboxException.

When thrown: Mailbox::enqueue() or Mailbox::dequeue() is called after the mailbox has been closed (actor stopped).

Cause: A message was sent to an actor that has already stopped. The tell() call races with the actor's shutdown.

Recovery: Check $ref->isAlive() before sending, or catch MailboxClosedException at the send site and route to dead letters manually. The HTTP default mapper returns 503 when this exception escapes a handler.

MailboxException

Monadial\Nexus\Runtime\Exception\MailboxException

Abstract base for mailbox-related exceptions. Extends RuntimeException. Catch to handle any mailbox failure.

MailboxOverflowException

Monadial\Nexus\Runtime\Exception\MailboxOverflowException

Extends MailboxException.

When thrown: OverflowStrategy::ThrowException is configured and the mailbox is at capacity when tell() is called.

Cause: The producer is faster than the consumer. The mailbox is bounded and the exception strategy is set to ThrowException.

Recovery: Catch at the send site and apply backoff, or switch to OverflowStrategy::Backpressure to suspend the sender instead. The HTTP default mapper returns 503 Service Unavailable when this exception escapes a handler.

See also: Config — OverflowStrategy

MailboxTimeoutException

Monadial\Nexus\Runtime\Exception\MailboxTimeoutException

Extends MailboxException.

When thrown: Mailbox::dequeueBlocking(Duration) returns after the timeout elapses with no message.

Cause: The actor's message loop waited for a message longer than the configured timeout. In normal operation, the runtime manages this internally.

Recovery: This exception is generally internal. If you see it in application code, you are calling dequeueBlocking() directly, which is a lower-level API. Let ActorCell manage the message loop.


nexus-persistence

ConcurrentModificationException

Monadial\Nexus\Persistence\Exception\ConcurrentModificationException

Extends RuntimeException.

When thrown: An optimistic-concurrency check fails in the event store: the expected version does not match the stored version.

Cause: Two actors (or two restarts of the same actor) attempted to write to the same persistence stream simultaneously, or a stale read occurred.

Recovery: Inspect $e->persistenceId and $e->expectedVersion. This exception signals a single-writer violation. Ensure only one actor instance writes to each PersistenceId. If using DBAL-backed stores, check database connection isolation.

See also: Single-writer guarantee

RecoveryException

Monadial\Nexus\Persistence\Exception\RecoveryException

Extends RuntimeException.

When thrown: The persistence engine fails to replay events or load a snapshot during actor startup.

Cause: Corrupted event data, incompatible serialized format after a schema change, or a missing event handler for a replayed event type.

Recovery: Inspect $e->persistenceId and the wrapped $previous exception. Consider using ReplayFilterMode::Warn to skip interleaved writer events rather than failing. For schema changes, deploy an event-migration step before restarting actors.

See also: Event sourcing

WriterConflictException

Monadial\Nexus\Persistence\Exception\WriterConflictException

Extends RuntimeException.

When thrown: The ReplayFilter detects events from a different writerId (ULID) than the current actor system's writer ID.

Cause: Two ActorSystem instances (separate processes or separate ActorSystem::create() calls) wrote to the same persistence stream. Violates the single-writer principle.

Recovery: Inspect $e->persistenceId, $e->expectedWriter, and $e->actualWriter. The ReplayFilterMode controls the response: Fail throws this exception, Warn logs it, RepairByDiscardOld silently drops the older writer's events.

See also: Single-writer guarantee


nexus-serialization

MessageDeserializationException

Monadial\Nexus\Serialization\Exception\MessageDeserializationException

Extends SerializationException.

When thrown: MessageSerializer::deserialize() fails to reconstruct a message object from its serialized form.

Cause: The serialized data is malformed, the target class no longer exists, or the class properties changed incompatibly after the data was written.

Recovery: Inspect $e->typeName and $e->reason. Implement event migrations or versioned message schemas before deploying breaking changes to message classes.

See also: Attributes — #[MessageType]

MessageSerializationException

Monadial\Nexus\Serialization\Exception\MessageSerializationException

Extends SerializationException.

When thrown: MessageSerializer::serialize() fails to convert a message object to its wire format.

Cause: The message class contains a property type that the serializer cannot handle (e.g., a Closure, a resource, or a circular reference).

Recovery: Inspect $e->messageClass and $e->reason. Ensure all message properties are serializable scalars, arrays, or other readonly class objects. Add #[MessageType] and test serialization round-trips in your test suite.

SerializationException

Monadial\Nexus\Serialization\Exception\SerializationException

Abstract base for all serialization failures. Extends NexusException. Catch to handle any serialization error.


nexus-doctrine-dbal

ConnectionPoisonedException

Monadial\Nexus\Doctrine\Dbal\Exception\ConnectionPoisonedException

Extends NexusException.

When thrown: A DBAL connection is used after it has been marked poisoned (e.g., a previous unrecoverable error put the connection in an invalid state).

Cause: A transaction rolled back in a way that left the connection unusable, or the database server dropped the connection mid-operation.

Recovery: Return the connection to the pool — the pool will discard poisoned connections and create fresh ones. Do not attempt to reuse a poisoned connection reference.

MissingConnectionScopeException

Monadial\Nexus\Doctrine\Dbal\Exception\MissingConnectionScopeException

Extends NexusException.

When thrown: A handler requests a ConnectionLease but ConnectionScopeMiddleware is not registered in the HTTP middleware pipeline.

Cause: ConnectionScopeMiddleware was not added to the application middleware chain before the route handler that needs a database connection.

Recovery: Add ConnectionScopeMiddleware globally: $app->middleware(new ConnectionScopeMiddleware($pool)).

MissingTransactionalDependencyException

Monadial\Nexus\Doctrine\Dbal\Exception\MissingTransactionalDependencyException

Extends NexusException.

When thrown: A handler annotated with #[Transactional] does not declare a Connection (or EntityManagerInterface) parameter.

Cause: #[Transactional] requires the handler to accept a connection parameter so the framework can inject the scoped connection and wrap the handler in a transaction.

Recovery: Add a Connection $connection or EntityManagerInterface $em parameter to the handler's invocation method. See the error message for the exact handler class.

See also: Attributes — #[Transactional]

PoolClosedException

Monadial\Nexus\Doctrine\Dbal\Exception\PoolClosedException

Extends NexusException.

When thrown: A connection is requested from a DBAL connection pool that has been shut down.

Cause: The pool was closed (e.g., during graceful shutdown) before all connection requests completed.

Recovery: Ensure connection acquisition happens before the pool is closed. During shutdown, drain pending requests before calling pool close.

PoolExhaustedException

Monadial\Nexus\Doctrine\Dbal\Exception\PoolExhaustedException

Extends NexusException.

When thrown: All connections in the pool are in use and no connection became available within the wait deadline.

Cause: Too many concurrent requests for the pool size, or long-running transactions holding connections.

Recovery: Inspect $e->stats (inUse, total, waitingCoroutines). Increase pool size via configuration, reduce transaction duration, or add a circuit breaker to shed load when the pool is exhausted.


nexus-doctrine-orm

EntityConflictException

Monadial\Nexus\Doctrine\Orm\Exception\EntityConflictException

Extends NexusException.

When thrown: Doctrine's optimistic locking fails — the entity's version field does not match the expected version at flush time.

Cause: Two concurrent requests updated the same entity between read and flush. The second flush triggers OptimisticLockException which is wrapped here.

Recovery: Catch EntityConflictException, reload the entity, re-apply the change, and retry. Inspect $e->entityClass and $e->id to identify the conflicting record.

MissingEntityManagerScopeException

Monadial\Nexus\Doctrine\Orm\Exception\MissingEntityManagerScopeException

Extends NexusException.

When thrown: A handler requests an EntityManagerLease but EntityManagerScopeMiddleware is not registered.

Cause: EntityManagerScopeMiddleware was omitted from the middleware pipeline.

Recovery: Add EntityManagerScopeMiddleware to the application: $app->middleware(new EntityManagerScopeMiddleware($emFactory)).


nexus-http-auth

AuthException

Monadial\Nexus\Http\Auth\Exception\AuthException

Abstract base for all auth exceptions. Extends NexusException. The default AuthorizationMiddleware maps subclasses to HTTP responses: Unauthenticated → 401, Forbidden → 403.

AuthMiddlewareNotRegisteredException

Monadial\Nexus\Http\Auth\Exception\AuthMiddlewareNotRegisteredException

Extends AuthException.

When thrown: A handler parameter uses #[FromPrincipal] but AuthenticationMiddleware was not registered in the pipeline — so no Principal was stamped on the request.

Cause: Missing AuthenticationMiddleware in the application middleware stack.

Recovery: Add $app->middleware(new AuthenticationMiddleware($authenticator)) before any route that uses authentication.

See also: Attributes — #[FromPrincipal]

Forbidden

Monadial\Nexus\Http\Auth\Exception\Forbidden

Extends AuthException. Mapped to HTTP 403 by AuthorizationMiddleware.

When thrown: AuthorizationMiddleware evaluates the principal's roles/scopes against the handler's #[RequiresRole], #[RequiresScope], or #[Authorize] attributes and finds a mismatch.

Payload: $missing — a list of the failed constraints. Never contains principal claims.

Recovery: Ensure the caller has the required roles/scopes before sending the request. On the server side, verify the #[RequiresRole]/#[RequiresScope] annotations are correct.

InvalidAuthorizerException

Monadial\Nexus\Http\Auth\Exception\InvalidAuthorizerException

Extends AuthException.

When thrown: At application boot time, #[Authorize(SomeClass::class)] is evaluated and SomeClass does not implement the Authorizer interface.

Cause: Programmer error — the wrong class was passed to #[Authorize].

Recovery: Implement Authorizer on the referenced class.

Unauthenticated

Monadial\Nexus\Http\Auth\Exception\Unauthenticated

Extends AuthException. Mapped to HTTP 401 by AuthorizationMiddleware.

When thrown: AuthorizationMiddleware finds no valid principal on the request (credentials absent or invalid).

Recovery: Present valid credentials. Customize the 401 response body via $app->onException(Unauthenticated::class, fn($e) => ...).


nexus-http

DuplicateActorNameException

Monadial\Nexus\Http\Exception\DuplicateActorNameException

Extends NexusException.

When thrown: HttpApp::actor() is called twice with the same actor name at boot time.

Cause: Duplicate registration — two calls to ->actor('name', ...) with the same string.

Recovery: Use unique names per actor registration. The second call triggers this exception.

DuplicateRouteNameException

Monadial\Nexus\Http\Exception\DuplicateRouteNameException

Extends NexusException.

When thrown: Two routes are registered with the same name.

Cause: ->route(...)->named('foo') is called for two different routes with 'foo'.

Recovery: Ensure route names are unique across the application. Names are used for URL generation.

GenericHttpException

Monadial\Nexus\Http\Exception\GenericHttpException

Extends HttpException. Concrete carrier used by HttpException factory methods (notFound(), forbidden(), etc.). Not typically thrown directly — use the factory methods on HttpException.

HttpException

Monadial\Nexus\Http\Exception\HttpException

Abstract base for HTTP-aware exceptions. Extends NexusException. Carries $status (HTTP status code) and $headers. The default exception mapper converts it directly to an HTTP response using the status code.

Factory methods: notFound() → 404, unauthorized() → 401, forbidden() → 403, unprocessableEntity(array $errors) → 422, conflict() → 409.

Recovery: Throw HttpException::notFound($message) from a handler to return a 404. Register a custom mapper via $app->onException(HttpException::class, ...) to override the default body format.

MethodNotAllowedException

Monadial\Nexus\Http\Exception\MethodNotAllowedException

Extends HttpException. HTTP 405 with Allow header.

When thrown: A request matches a registered path but the HTTP method is not allowed for that path.

Recovery: Check $e->allowed for the permitted methods. Return the 405 response with the Allow header (set automatically). Verify the client is using the correct HTTP method.

PerRequestActorInConstructorException

Monadial\Nexus\Http\Exception\PerRequestActorInConstructorException

Extends NexusException.

When thrown: A constructor parameter uses #[FromActor] to request a per-request actor. Per-request actors are only available inside the handler invocation method.

Cause: Per-request actors are spawned per-request, so they cannot exist at construction time.

Recovery: Move the #[FromActor] parameter from __construct to the handler's __invoke method.

PerRequestScopeDisposedException

Monadial\Nexus\Http\Exception\PerRequestScopeDisposedException

Extends NexusException.

When thrown: A per-request actor spawn is attempted after the request scope has been disposed (after the response is sent).

Cause: Spawning an actor inside an async callback that fires after response dispatch.

Recovery: Spawn all per-request actors before the response is returned. Do not spawn in after-response hooks.

PoolSingletonRequiresSpawnerException

Monadial\Nexus\Http\Exception\PoolSingletonRequiresSpawnerException

Extends NexusException.

When thrown: One or more actors are configured as poolSingleton() but no PoolSingletonSpawner is attached to HttpApp.

Cause: poolSingleton() actors require a server package that wires a spawner (e.g., the Swoole server integration). Missing integration step.

Recovery: Wire a PoolSingletonSpawner via $app->withSpawner(...), or change the actor mode to workerLocal().

RouteNotFoundException

Monadial\Nexus\Http\Exception\RouteNotFoundException

Extends HttpException. HTTP 404.

When thrown: No registered route matches the incoming request's method and path.

Recovery: Verify the route is registered. Check path prefix configuration if routes are mounted under a prefix. The default mapper returns a 404 response automatically.

UnknownActorException

Monadial\Nexus\Http\Exception\UnknownActorException

Extends NexusException.

When thrown: A handler or scope requests an actor by name that was not registered with HttpApp::actor().

Cause: Typo in the actor name string, or the actor registration was removed while the handler still references it.

Recovery: Verify the actor name string matches exactly what was passed to ->actor('name', ...). Actor names are case-sensitive.

UnresolvableParameterException

Monadial\Nexus\Http\Handler\Resolver\Exception\UnresolvableParameterException

Extends LogicException.

When thrown: At compile/boot time, a handler parameter cannot be resolved by any registered ParamResolver.

Cause: The parameter has no recognized attribute (#[FromBody], #[FromActor], #[FromService], #[FromPrincipal]), is not a path parameter, and is not type-hinted to a known injectable type.

Recovery: Add the appropriate attribute to the parameter. The exception message lists all valid resolution strategies. Register a custom ParamResolver via $app->paramResolver(...) for domain-specific injection.


nexus-http-ws

DuplicateRouteException

Monadial\Nexus\Http\Ws\WebSocket\Exception\DuplicateRouteException

Extends RuntimeException.

When thrown: Two WebSocket routes are registered with the same path pattern.

Recovery: Ensure each WebSocket path is unique in the WebSocket route registry.

UnsupportedRouteException

Monadial\Nexus\Http\Ws\WebSocket\Exception\UnsupportedRouteException

Extends RuntimeException.

When thrown: A WebSocket upgrade request arrives for a path that has no registered WebSocket handler.

Recovery: Register a handler for the path before the server starts. If the path is intentionally absent, return an HTTP 404 before the WebSocket upgrade completes.


Untyped throws

Beyond the 41 typed exception classes above, the Nexus codebase contains approximately 128 sites that throw standard PHP exceptions — RuntimeException, LogicException, and InvalidArgumentException — directly without a Nexus-specific subclass. These are concentrated in:

  • Internal runtime plumbing (fiber scheduling, Swoole coroutine management)
  • Validation guards on named constructors (e.g., WorkerPoolConfig::withThreads() throws InvalidArgumentException for $workerCount < 1)
  • Third-party integration adapters (Doctrine DBAL driver errors not yet wrapped)

These untyped throws are not part of the public API contract. They may be wrapped in typed NexusException subclasses in future versions. When catching exceptions from Nexus code broadly, catch NexusException for the typed surface and \Throwable as a fallback.


See also