Common Problems
Use the exact error name or start with the part of Event Nest that is failing.
Find your problem
- Nest wiring: Missing event publisher ,
EVENT_STOREcannot be injected - Event registration and replay: Unregistered event on append ,
UnknownEventExceptionduring replay , undefined@ApplyEventclass , event name conflict , missing aggregate name - Concurrency and versions: Concurrent write conflict , invalid explicit aggregate version
- Snapshots: Incomplete configuration , aggregate is not snapshot-aware , invalid strategy construction , revision mismatch , load resumes at version 0
- Subscriptions: Handlers are not discovered , asynchronous failure does not reject
commit(),SubscriptionExceptionafter persistence - Storage adapters: SQL tables are not created , MongoDB transaction failure , MongoDB rejects an aggregate ID , PostgreSQL SSL failure , SQL Server configuration rejection
Nest wiring
Missing event publisher
Symptom: Calling commit() rejects with the string There is no event publisher assigned.
Likely cause: The aggregate was constructed or reconstituted directly and never connected to an EventStore.
How to confirm: Check whether the aggregate came from AggregateRepository.load() or was passed through eventStore.addPublisher(). Merely injecting the store elsewhere does not modify the aggregate.
Resolution: Call eventStore.addPublisher(aggregate) before commit(), use AggregateRepository.save(), or load through AggregateRepository, which returns an already connected instance. A commit with no uncommitted events is a no-op and does not invoke the publisher.
Related docs: Public API and AggregateRepository .
EVENT_STORE cannot be injected
Symptom: Nest reports that it cannot resolve the EVENT_STORE dependency.
Likely cause: A scoped register/registerAsync module is not imported where the consumer is declared, or an async factory dependency is not visible. Async option types do not support an imports field.
How to confirm: Check whether registration used forRoot* (global) or register* (scoped), and whether every token in inject is already available in that module context.
Resolution: Import the scoped dynamic module into the consumer's module, re-export it as needed, or use one global forRoot* registration. Make async factory dependencies global or otherwise visible before registration.
Related docs: Registration methods and Public API .
Event registration and replay
Unregistered event on append
Symptom: append() reports that an event class is not registered.
Likely cause: The class lacks @DomainEvent, the decorated module was never imported at runtime, or a different class copy/constructor was instantiated.
How to confirm: Verify the runtime class has the decorator and that the file is included in the application startup graph. Registration is process-local and keyed by constructor identity for writes.
Resolution: Decorate the event with a globally unique persisted name and import its defining module before using it. The thrown class is internal UnregisteredEventException and is not a public package export.
Related docs: Decorators and Exceptions .
UnknownEventException during replay
Symptom: Reconstitution lists names under Unregistered or Missing apply method.
Likely cause: Unregistered means no loaded @DomainEvent registration matches the stored canonical name/alias. Missing apply method means the event class resolved, but this aggregate has no @ApplyEvent metadata for that exact class.
How to confirm: Inspect both lists in the exception message and compare database event_name values with decorators. Confirm the replay handler imports the same event class object.
Resolution: Load/register the event class, retain historical names in aliases, and add the missing @ApplyEvent(EventClass) handler. Do not edit historical event names without a deliberate migration.
Related docs: Decorators , Event aliases , and Exceptions .
Undefined @ApplyEvent class
Symptom: Application initialization reports that @ApplyEvent was used with a null or undefined event class.
Likely cause: A circular dependency or incorrect import left the event binding undefined when decorator evaluation ran.
How to confirm: Log or test the imported constructor before the aggregate class declaration and inspect import cycles. This fails when the decorator factory runs, not during event replay.
Resolution: Correct the import or break the cycle while retaining the same event class identity. The thrown MissingEventClassException is internal and cannot be imported from the public barrel.
Related docs: Decorators and Exceptions .
Event name conflict
Symptom: Startup throws EventNameConflictException before a command is handled.
Likely cause: A canonical event name or alias duplicates another canonical name/alias, or the same value appears twice in one event's registration.
How to confirm: Search all @DomainEvent names and aliases for the name in the exception message. Include libraries loaded more than once in the same process.
Resolution: Make every canonical name and alias globally unique. Keep aliases durable while historical events still use them.
Related docs: Decorators .
Missing aggregate name
Symptom: MissingAggregateRootNameException occurs during publish, event lookup, or snapshot lookup.
Likely cause: The aggregate class has no name metadata or the wrong undecorated class was supplied to the store.
How to confirm: Check the concrete class passed to findByAggregateRootId, findWithSnapshot, or addPublisher; inheritance/import mistakes can hide the expected decorated class.
Resolution: Add @AggregateRootConfig({ name: "DurableName" }). Preserve a name already stored in events. Although the exception text may mention @AggregateRootName, that decorator is deprecated and planned for removal in 7.x.
Related docs: Decorators .
Concurrency and versions
Concurrent write conflict
Symptom: A commit reports different expected and stored aggregate versions.
Likely cause: Another writer committed after this aggregate was loaded, the application reused a stale instance, or a snapshot-at-stream-head aggregate was reconstituted at version 0.
How to confirm: Compare the exception's expected/stored numbers with findAggregateRootVersion(id). For snapshot loads with zero newer events, confirm the factory receives and forwards aggregateRootVersion.
Resolution: Reload and re-run the business command only if retrying is safe. Prefer AggregateRepository, or pass all four factory values through to reconstitute(events, snapshot, aggregateRootVersion). Never bypass the version check.
Related docs: Exceptions , Event streams and versions , and Snapshot loading .
Invalid explicit aggregate version
Symptom: Reconstitution says aggregateRootVersion must be a non-negative integer.
Likely cause: A custom store/factory passed a negative, fractional, unsafe, or non-numeric value as the third argument.
How to confirm: Inspect the fourth factory argument and third reconstitute() argument. Omitting it is valid; when supplied it must satisfy Number.isSafeInteger(value) and be at least zero.
Resolution: Return the authoritative non-negative stream version from the store, or omit it only when replayed events can correctly determine the version.
Related docs: Public API and Exceptions .
Snapshots
Incomplete snapshot configuration
Symptom: Provider creation says both snapshot fields must be supplied.
Likely cause: Only snapshotStrategy or only the adapter storage field was configured.
How to confirm: For MongoDB inspect snapshotStrategy plus snapshotCollection; for PostgreSQL/SQL Server inspect snapshotStrategy plus snapshotTableName. This is enforced by the option union and repeated at runtime for sync and async registration.
Resolution: Provide the pair or remove both to install NoOpSnapshotStore. There is no shared public snapshot-options object to configure separately.
Related docs: Configuration and Exceptions .
Aggregate is not snapshot-aware
Symptom: AggregateClassNotSnapshotAwareException or AggregateInstanceNotSnapshotAwareException occurs when a strategy matches.
Likely cause: The class omits numeric snapshotRevision, or the instance lacks callable toSnapshot() or applySnapshot() methods. A broad strategy can also match an aggregate not intended for snapshots.
How to confirm: Check all three requirements independently: aggregate name, numeric class revision, and both instance methods. TypeScript implements SnapshotAware does not exist at runtime.
Resolution: Complete @AggregateRootConfig and SnapshotAware, or compose the policy with ForAggregateRootsStrategy so only ready aggregates match.
Related docs: Decorators , Snapshot policies , and Exceptions .
Invalid snapshot strategy construction
Symptom: Startup reports that count may not be less than 1 or that a composite strategy requires at least one strategy.
Likely cause: ForCountSnapshotStrategy received count < 1, or AllOfSnapshotStrategy/AnyOfSnapshotStrategy received an empty array.
How to confirm: Inspect the values used to construct the strategy, especially environment-derived counts and conditionally assembled arrays.
Resolution: Use a positive count and at least one child strategy. Use NoSnapshotStrategy or omit the adapter snapshot pair when snapshots should be disabled.
Related docs: Public API and Configuration .
Snapshot revision mismatch
Symptom: Direct findWithSnapshot() throws SnapshotRevisionMismatchException after a deployment.
Likely cause: The aggregate's configured revision differs from the latest stored snapshot revision.
How to confirm: Compare @AggregateRootConfig({ snapshotRevision }) with the snapshot row/document revision. Also check whether mixed application versions are active.
Resolution: Fall back to findByAggregateRootId() and full replay, then allow a new snapshot to be produced. AggregateRepository.load() already catches this one exception and performs the full replay; other snapshot errors are rethrown.
Related docs: Exceptions and Snapshot loading .
Snapshot load resumes at version 0
Symptom: An aggregate loaded from a snapshot with no later events has correct state but its next commit conflicts, often expecting version 0.
Likely cause: The factory ignored aggregateRootVersion returned by findWithSnapshot(). With no replayed events, reconstitute() cannot infer the snapshot's stream version from the event array.
How to confirm: Log the read result and aggregate version immediately after factory construction. The built-in adapters return the snapshot version when the post-snapshot event list is empty.
Resolution: Define the factory as (id, events, snapshot, aggregateRootVersion) and call root.reconstitute(events, snapshot, aggregateRootVersion), or use AggregateRepository with a factory that forwards all arguments.
Related docs: Public API and Snapshot loading .
Subscriptions
Handlers are not discovered
Symptom: Event Nest logs that no subscriptions were discovered, or a particular event cannot be passed to subscriptions.
Likely cause: The handler is not a Nest provider, lacks @DomainEventSubscription, lacks a callable onDomainEvent, or the application has not completed bootstrap.
How to confirm: Confirm the class appears in a module's providers, has both decorator and method, and the application was initialized. Binding scans instantiated providers during adapter onApplicationBootstrap.
Resolution: Register the decorated handler as a provider and implement OnDomainEvent<T>. Ensure the adapter module is imported once in the relevant/global scope and allow application bootstrap to finish before committing.
Related docs: Decorators and Configuration .
Asynchronous failure does not reject commit()
Symptom: A handler logs an error, but commit() has already resolved.
Likely cause: isAsync defaults to true, so asynchronous subscriptions normally do not delay commits. With concurrentSubscriptions: true, their failures are logged and swallowed by the detached dispatch path.
How to confirm: Inspect the decorator configuration and module's concurrentSubscriptions. Remember that isAsync describes whether commit waits, not whether onDomainEvent returns a promise (it always must).
Resolution: Use @DomainEventSubscription({ eventClasses: [...], isAsync: false }) when the caller must observe handler failure. Make side effects idempotent and monitor asynchronous failures when eventual consistency is intended.
Related docs: Decorators and Exceptions .
SubscriptionException after persistence
Symptom: An awaited subscription fails and the caller assumes the event transaction rolled back.
Likely cause: Event Nest persists events, resolves versions, and creates any snapshot before dispatching subscriptions.
How to confirm: Use eventId from the exception to inspect persisted events. The exception also exposes caughtError and eventClassName.
Resolution: Do not append or commit the same domain event again. Repair/retry the projection or side effect idempotently. Event Nest clears the aggregate's uncommitted events for SubscriptionException specifically.
Related docs: Exceptions and Failure behaviour .
Storage adapters
SQL tables are not created
Symptom: Tables remain absent, or bootstrap logs a DDL/permission failure.
Likely cause: ensureTablesExist defaults to false, the configured schema does not exist, the database user lacks DDL rights, or names point at another schema.
How to confirm: Check the resolved async options and database permissions. PostgreSQL logs table-initialization errors and continues; SQL Server logs and rethrows them, so startup fails.
Resolution: Create the schema first and grant appropriate rights, set ensureTablesExist: true, or create the documented tables manually and leave the option false. Initialization creates missing tables; it is not a migration engine for existing tables.
Related docs: Configuration and Storage model .
MongoDB transaction failure
Symptom: Save or purge fails with a message that transactions are unsupported or require a replica set.
Likely cause: The MongoDB adapter runs both operations in a session transaction, but the server is standalone or the deployment/URI does not support transactions.
How to confirm: Inspect the MongoDB topology and run the driver's transaction capability checks. A successful connection alone does not prove transaction support.
Resolution: Use a replica set or supported sharded deployment and a URI that selects it. Do not work around the error by bypassing Event Nest's transaction, because aggregate version and event atomicity depend on it.
Related docs: MongoDB configuration and Compatibility .
MongoDB rejects an aggregate ID
Symptom: Reads, saves, or purges fail while constructing a MongoDB ObjectId.
Likely cause: The MongoDB adapter expects aggregate IDs accepted by new ObjectId(id), but application code supplied another ID format.
How to confirm: Check that the ID is a 24-character hexadecimal ObjectId string. A UUID, including one returned by randomUUID(), is not accepted by this adapter.
Resolution: Create aggregate IDs in application code with the MongoDB driver's new ObjectId().toHexString(), and validate externally supplied IDs before store calls.
Related docs: Public API and MongoDB configuration .
PostgreSQL SSL failure
Symptom: TLS negotiation or certificate validation fails after setting ssl.
Likely cause: The CA text is incorrect, rejectUnauthorized does not match the deployment, or SSL was configured both in the URI and explicit options inconsistently.
How to confirm: Inspect the exact resolved SslOptions. Event Nest maps certificate to ssl.ca and forwards the required rejectUnauthorized; when ssl is omitted it does not add an SSL object.
Resolution: Supply the correct CA certificate string and validation policy for the server, or omit explicit ssl when URI/driver defaults should control transport.
Related docs: PostgreSQL configuration and Compatibility .
SQL Server configuration rejection
Symptom: Startup rejects identifiers, or reports that port and instanceName cannot both be provided.
Likely cause: A schema/table field is blank, over 128 characters, contains a dot, or the connection tries to select a named instance and explicit port simultaneously.
How to confirm: Validate schemaName, all table names, and connection. Event Nest expects schema and table as separate values and defaults encrypt to true, trustServerCertificate to false, and pool min to 0.
Resolution: Remove schema prefixes from table fields, use legal SQL Server identifiers, and choose either port or instanceName. Configure TLS explicitly rather than disabling validation without understanding the risk.
Related docs: SQL Server configuration and Compatibility .