# Common Mistakes

This guide describes **common mistakes and misunderstandings** that arise when working with EventSourcingDB. Each section highlights a **specific problem**, explains its background, and **offers guidance on how to avoid or resolve it**. For mistakes in event sourcing in general – such as vague event names, missing idempotency, or personal data in events – see **[Common Mistakes](/concepts/common-mistakes)** in Concepts.

## Expecting to Delete Events Later

EventSourcingDB does not support **deletion**. Once written, events remain part of the system permanently. If data must be removed, the only option is to **migrate to a new instance using a filtered backup and restore process**.

Designing systems under the assumption that events can be deleted leads to architectural problems. Deletion must be modeled explicitly – for example, by writing a `user-erased` or `consent-withdrawn` event, rather than trying to remove existing data.

## Misunderstanding How Signatures Work *(Added in 1.1)*

Some developers are surprised when **signature verification fails**, even though signing is enabled and the event hasn't been modified. This usually stems from a misunderstanding: Signatures in EventSourcingDB are **not stored** but **generated dynamically** using the **currently active signing key**. If the key changes, the same event yields a **different signature**. To verify an event after a key rotation, it must be **fetched again**.

This design avoids **persisting cryptographic material** and keeps the system **stateless**. However, it also means that signatures are only valid for **short-term authenticity checks**. For **long-term verification** or **audit trails**, rely on the event's **stable `hash`**, which remains unchanged regardless of key rotations.

A **valid hash** confirms the **event content is unmodified**. A **valid signature** proves the event was **issued by a trusted instance**. Only if **both are valid** can the event be considered **intact and authentic**. If a **signature is missing**, the event was either **unsigned** or the signature was **stripped** – and its **authenticity should not be assumed**.

## Writing Without Preconditions

Concurrent writes to the same subject can produce **inconsistent or conflicting histories** if no preconditions are used. Clients should use **optimistic concurrency control** to prevent race conditions.

*(Changed in 1.2)* EventSourcingDB supports four preconditions: `isSubjectPristine`, `isSubjectPopulated`, `isSubjectOnEventId` and `isEventQlQueryTrue`. Use them to assert the expected stream state before writing. This ensures that only one client can successfully write under a given condition.

## Registering a Schema Too Early

Registering a schema too early in the development process can cause long-term friction. Once a schema is registered in EventSourcingDB, it becomes **immutable**. If the event structure later changes – for example, because of new business requirements or adjustments in naming – the original schema cannot be altered or removed. The only option is to introduce a **new event type with a different version**.

This can clutter your event log with unnecessary versioning and introduce unnecessary complexity that could have been avoided. Before registering a schema, ensure that the event model has **stabilized**. Prototyping events without schema validation during the early phases can help explore the domain more freely. Once the structure and semantics are clear, schema enforcement becomes valuable for **consistency and safety**.

## Registering a Schema Too Late

If a schema is registered after many events have already been written, and those events do not match the schema, the registration will **fail**. EventSourcingDB enforces that all past events conform to the schema.

To avoid this, **register schemas early** – ideally before any production data is written. This ensures that data is validated consistently and that schema registration succeeds on first attempt.

## Overusing Subject Recursion

Recursive reads allow querying all sub-subjects, but this should be used carefully. Reading from `/` with recursion enabled loads **all events – which may be millions** – and is not suitable for all use cases.

Use recursive reads for **projections or analytics**, not for simple stream access. When working with a specific subject, **disable recursion** unless you need substreams.

## Misusing fromLatestEvent

The `fromLatestEvent` option is powerful but has limitations. It only works in **chronological order** and cannot be combined with `lowerBound`. It also assumes the event type you're referencing **exists**.

Using it without understanding these constraints can result in **empty results or unexpected reads**. Read the documentation carefully before using it in production.

## Exposing EventSourcingDB Publicly

EventSourcingDB is designed for use inside **trusted environments**. It uses a **single shared API token** and should not be exposed to the public internet.

Deploy it behind **firewalls**, within **private networks**, or using an **API gateway**. Never allow **unauthenticated access** or rely solely on obscurity.

## Using `latest` as Docker Tag

Using the `latest` tag when deploying EventSourcingDB is **risky and not recommended**. While it may seem convenient, it introduces a **hidden dependency** on the current state of the Docker registry. If a new version is pushed and `latest` points to it, your deployment may unexpectedly change – even if you made no modifications yourself.

This undermines **reproducibility and stability**, which are essential properties of event-sourced systems. Since EventSourcingDB guarantees **deterministic behavior and strong consistency**, upgrading the database should always be a **conscious decision**, not an accidental side effect of using an unpinned tag.

Always use a **specific version tag** (e.g. `1.2.0`) when pulling or deploying the image. This ensures **predictable behavior across environments**, simplifies debugging, and avoids surprises during testing or recovery.

## Assuming EventSourcingDB Provides Read Models

EventSourcingDB **stores events**. It does not maintain **projections**, **aggregates**, or **queryable views**. These must be implemented in the application.

Expecting to "query" current state directly from the event store reflects a misunderstanding. Build and maintain **read models explicitly** through event processing logic.

## Expecting Exactly Once Delivery

EventSourcingDB provides **at-least-once delivery** for observers. It does not guarantee **exactly once semantics**. This responsibility lies with the application, which must implement **deduplication and persistence mechanisms** as needed.

Use **idempotent handlers** and **persistent tracking** to avoid duplicate side effects. Design for at-least-once delivery and promote **resilience through clear contracts**.

## Using EventQL for Live Event Processing

EventQL is designed for **analyzing and transforming historical data**. It is not a substitute for **live observation or streaming systems**.

Use the observation API when you need to **react to events as they happen**. Use EventQL for **projections, exports, and query-based reporting**.

## Management UI Not Accessible in Browser

If the management UI is not accessible in your browser but the API works correctly (e.g., `curl` requests succeed), check if you're using a **browser-blocked port**.

Browsers block certain ports for security reasons according to the **[WHATWG Fetch specification](https://fetch.spec.whatwg.org/#port-blocking)**. Common examples include **port 22 (SSH)**, **port 25 (SMTP)**, **port 6000 (X11)**, and **port 6665 (IRC)**.

To solve this issue, restart EventSourcingDB using a different port, such as **3000**, **4000**, **8080**, or **8443**. For more information on changing ports, see **[Running EventSourcingDB](/docs/eventsourcingdb/running-eventsourcingdb)**.
