Skip to content

fix: improve error message consistency, context, and safety across all crates#352

Merged
jwilger merged 2 commits intomainfrom
chore/error-message-consistency-audit
Apr 12, 2026
Merged

fix: improve error message consistency, context, and safety across all crates#352
jwilger merged 2 commits intomainfrom
chore/error-message-consistency-audit

Conversation

@jwilger-ai-bot
Copy link
Copy Markdown
Collaborator

Summary

Comprehensive error message consistency audit and fixes across all 8 EventCore crates. Closes #243.

Changes

  • VersionConflict context: EventStoreError::VersionConflict now carries stream_id, expected, and actual fields instead of being a bare unit variant with no context
  • Production panics removed: Replaced .expect() in SQLite append_events() and try_acquire_lock() with proper Result-based error handling
  • Cross-backend coordination consistency: All three backends (postgres, sqlite, memory) now include subscription_name in leadership error messages
  • thiserror adoption: Converted InMemoryCheckpointError, InMemoryCoordinationError, NoCheckpointError, and ContractTestFailure from manual impl Display to #[derive(thiserror::Error)]
  • Macro error messages differentiated: Three formerly-identical compile error messages now distinguish between non-struct types, tuple structs, and missing field identifiers
  • Example anti-pattern fixed: single_stream_command_test.rs now uses typed WithdrawError enum with From impl instead of direct CommandError::BusinessRuleViolation(format!(...))

Not changed (intentional)

  • CommandError::BusinessRuleViolation(String) and its From<String>/From<&str> impls — intentional design for the require! macro
  • ProjectionError::Failed(String) structure — would require pipeline refactoring beyond audit scope
  • EventStoreError::StoreFailure detail field — needs deeper architectural thought about error source chains

Test plan

  • All 209 tests pass (cargo nextest run --workspace)
  • Clippy clean (cargo clippy --all-targets --all-features -- -D warnings)
  • Format clean (cargo fmt --all --check)
  • Trybuild UI tests updated for new macro error messages
  • Self-review against REVIEW.md and .claude/rules/ — no violations

…l crates

- Add context fields (stream_id, expected, actual) to
  EventStoreError::VersionConflict across all backends (memory, sqlite,
  postgres) and update all pattern matches throughout the workspace
- Remove production panics: replace .expect() in SQLite's
  try_acquire_lock (now returns LockPoisoned error) and
  append_events version counter lookup (now returns UndeclaredStream)
- Fix cross-backend coordination error inconsistency: all three backends
  now include subscription_name in LeadershipNotAcquired error messages
- Convert manual Display impls to thiserror derives: InMemoryCheckpointError,
  InMemoryCoordinationError, NoCheckpointError, ContractTestFailure
- Differentiate duplicate macro error messages: non-struct vs tuple struct
  errors now have distinct messages in #[derive(Command)]
- Fix example anti-pattern: Withdraw::handle in single_stream_command_test
  now uses typed WithdrawError enum instead of direct
  CommandError::BusinessRuleViolation construction
@jwilger jwilger enabled auto-merge (squash) April 12, 2026 04:19
Comment thread eventcore-examples/tests/single_stream_command_test.rs Outdated
Change BusinessRuleViolation(String) to BusinessRuleViolation(Box<dyn Error>)
to preserve the Rust error chain convention. From impls now use Box::new(e)
instead of e.to_string(). Added BusinessRuleMessage helper type for
string-based errors from the require! macro.
@jwilger jwilger merged commit f188448 into main Apr 12, 2026
19 checks passed
@jwilger jwilger deleted the chore/error-message-consistency-audit branch April 12, 2026 05:13
@jwilger jwilger mentioned this pull request Apr 12, 2026
jwilger added a commit that referenced this pull request Apr 13, 2026
## 🤖 New release

* `eventcore-macros`: 0.6.0 -> 0.7.0
* `eventcore-types`: 0.6.0 -> 0.7.0 (⚠ API breaking changes)
* `eventcore-postgres`: 0.6.0 -> 0.7.0 (⚠ API breaking changes)
* `eventcore-sqlite`: 0.6.0 -> 0.7.0 (⚠ API breaking changes)
* `eventcore`: 0.6.0 -> 0.7.0 (⚠ API breaking changes)
* `eventcore-memory`: 0.6.0 -> 0.7.0 (⚠ API breaking changes)
* `eventcore-testing`: 0.6.0 -> 0.7.0 (✓ API compatible changes)
* `eventcore-examples`: 0.6.0 -> 0.7.0 (✓ API compatible changes)

### ⚠ `eventcore-types` breaking changes

```text
--- failure auto_trait_impl_removed: auto trait no longer implemented ---

Description:
A public type has stopped implementing one or more auto traits. This can break downstream code that depends on the traits being implemented.
        ref: https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/auto_trait_impl_removed.ron

Failed in:
  type CommandError is no longer UnwindSafe, in /tmp/.tmp9Ta52e/eventcore/eventcore-types/src/errors.rs:21
  type CommandError is no longer RefUnwindSafe, in /tmp/.tmp9Ta52e/eventcore/eventcore-types/src/errors.rs:21

--- failure enum_unit_variant_changed_kind: An enum unit variant changed kind ---

Description:
A public enum's exhaustive unit variant has changed to a different kind of enum variant, breaking possible instantiations and patterns.
        ref: https://doc.rust-lang.org/reference/items/enumerations.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/enum_unit_variant_changed_kind.ron

Failed in:
  variant EventStoreError::VersionConflict in /tmp/.tmp9Ta52e/eventcore/eventcore-types/src/store.rs:381

--- failure trait_added_supertrait: non-sealed trait added new supertraits ---

Description:
A non-sealed trait added one or more supertraits, which breaks downstream implementations of the trait
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#generic-bounds-tighten
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/trait_added_supertrait.ron

Failed in:
  trait eventcore_types::CommandLogic gained Send in file /tmp/.tmp9Ta52e/eventcore/eventcore-types/src/command.rs:149
  trait eventcore_types::CommandLogic gained Sync in file /tmp/.tmp9Ta52e/eventcore/eventcore-types/src/command.rs:149

--- failure trait_method_added: pub trait method added ---

Description:
A non-sealed public trait added a new method without a default implementation, which breaks downstream implementations of the trait
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#trait-new-item-no-default
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/trait_method_added.ron

Failed in:
  trait method eventcore_types::Event::event_type_name in file /tmp/.tmp9Ta52e/eventcore/eventcore-types/src/command.rs:128
```

### ⚠ `eventcore-postgres` breaking changes

```text
--- failure enum_unit_variant_changed_kind: An enum unit variant changed kind ---

Description:
A public enum's exhaustive unit variant has changed to a different kind of enum variant, breaking possible instantiations and patterns.
        ref: https://doc.rust-lang.org/reference/items/enumerations.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/enum_unit_variant_changed_kind.ron

Failed in:
  variant CoordinationError::LeadershipNotAcquired in /tmp/.tmp9Ta52e/eventcore/eventcore-postgres/src/lib.rs:540
```

### ⚠ `eventcore-sqlite` breaking changes

```text
--- failure enum_variant_added: enum variant added on exhaustive enum ---

Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/enum_variant_added.ron

Failed in:
  variant SqliteCoordinationError:LockPoisoned in /tmp/.tmp9Ta52e/eventcore/eventcore-sqlite/src/lib.rs:57
```

### ⚠ `eventcore` breaking changes

```text
--- failure enum_missing: pub enum removed or renamed ---

Description:
A publicly-visible enum cannot be imported by its prior path. A `pub use` may have been removed, or the enum itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/enum_missing.ron

Failed in:
  enum eventcore::PollMode, previously in file /tmp/.tmpiyxp8q/eventcore/src/projection.rs:101

--- failure function_parameter_count_changed: pub fn parameter count changed ---

Description:
A publicly-visible function now takes a different number of parameters.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#fn-change-arity
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/function_parameter_count_changed.ron

Failed in:
  eventcore::run_projection now takes 3 parameters instead of 2, in /tmp/.tmp9Ta52e/eventcore/eventcore/src/projection.rs:586

--- failure struct_missing: pub struct removed or renamed ---

Description:
A publicly-visible struct cannot be imported by its prior path. A `pub use` may have been removed, or the struct itself may have been renamed or removed entirely.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/struct_missing.ron

Failed in:
  struct eventcore::NoCheckpointStore, previously in file /tmp/.tmpiyxp8q/eventcore/src/projection.rs:161
  struct eventcore::ProjectionRunner, previously in file /tmp/.tmpiyxp8q/eventcore/src/projection.rs:141
  struct eventcore::PollConfig, previously in file /tmp/.tmpiyxp8q/eventcore/src/projection.rs:26
  struct eventcore::EventRetryConfig, previously in file /tmp/.tmpiyxp8q/eventcore/src/projection.rs:72
```

### ⚠ `eventcore-memory` breaking changes

```text
--- failure struct_with_no_pub_fields_changed_type: public API struct with no public fields is no longer a struct ---

Description:
A struct without pub fields became an enum or union, breaking pattern matching.
        ref: https://internals.rust-lang.org/t/rest-patterns-foo-should-match-non-struct-types/21607
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.47.0/src/lints/struct_with_no_pub_fields_changed_type.ron

Failed in:
  struct eventcore_memory::InMemoryCheckpointError became enum in file /tmp/.tmp9Ta52e/eventcore/eventcore-memory/src/lib.rs:324
```

<details><summary><i><b>Changelog</b></i></summary><p>

## `eventcore-macros`

<blockquote>

##
[0.7.0](eventcore-macros-v0.6.0...eventcore-macros-v0.7.0)
- 2026-04-13

### Bug Fixes

- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- enhance require! macro to accept typed error values
([#335](#335))
- add required event_type_name() to Event trait for stable storage
([#344](#344))

### Miscellaneous Tasks

- adopt han plugins, blueprints, and project conventions
([#330](#330))
- consolidate workspace lints and enforce strict lint policy
([#351](#351))

### Refactoring

- expose projection config via free function API, then reduce public
surface ([#357](#357))
</blockquote>

## `eventcore-types`

<blockquote>

##
[0.7.0](eventcore-types-v0.6.0...eventcore-types-v0.7.0)
- 2026-04-13

### Bug Fixes

- add Send+Sync bounds to CommandLogic for Send futures
([#332](#332))
- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- enhance require! macro to accept typed error values
([#335](#335))
- add required event_type_name() to Event trait for stable storage
([#344](#344))

### Miscellaneous Tasks

- consolidate workspace lints and enforce strict lint policy
([#351](#351))

### Refactoring

- replace into_inner() with into() for nutype domain types
([#334](#334))
</blockquote>

## `eventcore-postgres`

<blockquote>

##
[0.7.0](eventcore-postgres-v0.6.0...eventcore-postgres-v0.7.0)
- 2026-04-13

### Bug Fixes

- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- add required event_type_name() to Event trait for stable storage
([#344](#344))

### Miscellaneous Tasks

- adopt han plugins, blueprints, and project conventions
([#330](#330))
- consolidate workspace lints and enforce strict lint policy
([#351](#351))
</blockquote>

## `eventcore-sqlite`

<blockquote>

##
[0.7.0](eventcore-sqlite-v0.6.0...eventcore-sqlite-v0.7.0)
- 2026-04-13

### Bug Fixes

- apply PRAGMA key before WAL mode in SQLite encrypted stores
([#333](#333))
- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Miscellaneous Tasks

- consolidate workspace lints and enforce strict lint policy
([#351](#351))
</blockquote>

## `eventcore`

<blockquote>

##
[0.7.0](eventcore-v0.6.0...eventcore-v0.7.0)
- 2026-04-13

### Bug Fixes

- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- enhance require! macro to accept typed error values
([#335](#335))
- add required event_type_name() to Event trait for stable storage
([#344](#344))

### Miscellaneous Tasks

- adopt han plugins, blueprints, and project conventions
([#330](#330))
- consolidate workspace lints and enforce strict lint policy
([#351](#351))

### Refactoring

- replace into_inner() with into() for nutype domain types
([#334](#334))
- extract pure state machines from execute() and run()
([#349](#349))
- expose projection config via free function API, then reduce public
surface ([#357](#357))
</blockquote>

## `eventcore-memory`

<blockquote>

##
[0.7.0](eventcore-memory-v0.6.0...eventcore-memory-v0.7.0)
- 2026-04-13

### Bug Fixes

- make InMemoryEventStore error on read_stream type mismatch
([#342](#342))
- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- add required event_type_name() to Event trait for stable storage
([#344](#344))

### Miscellaneous Tasks

- consolidate workspace lints and enforce strict lint policy
([#351](#351))
</blockquote>

## `eventcore-testing`

<blockquote>

##
[0.7.0](eventcore-testing-v0.6.0...eventcore-testing-v0.7.0)
- 2026-04-13

### Bug Fixes

- make InMemoryEventStore error on read_stream type mismatch
([#342](#342))
- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- add required event_type_name() to Event trait for stable storage
([#344](#344))
- add TestScenario GWT testing helpers to eventcore-testing
([#346](#346))

### Miscellaneous Tasks

- consolidate workspace lints and enforce strict lint policy
([#351](#351))

### Refactoring

- expose projection config via free function API, then reduce public
surface ([#357](#357))
</blockquote>

## `eventcore-examples`

<blockquote>

##
[0.7.0](eventcore-examples-v0.6.0...eventcore-examples-v0.7.0)
- 2026-04-13

### Bug Fixes

- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- add required event_type_name() to Event trait for stable storage
([#344](#344))
- add TestScenario GWT testing helpers to eventcore-testing
([#346](#346))

### Miscellaneous Tasks

- consolidate workspace lints and enforce strict lint policy
([#351](#351))

### Refactoring

- replace into_inner() with into() for nutype domain types
([#334](#334))
- expose projection config via free function API, then reduce public
surface ([#357](#357))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
@jwilger jwilger mentioned this pull request Apr 15, 2026
jwilger-ai-bot pushed a commit that referenced this pull request Apr 17, 2026
## 🤖 New release

* `eventcore-macros`: 0.7.0 -> 0.7.1
* `eventcore-types`: 0.7.0 -> 0.7.1 (✓ API compatible changes)
* `eventcore-postgres`: 0.7.0 -> 0.7.1 (✓ API compatible changes)
* `eventcore-sqlite`: 0.7.0 -> 0.7.1 (✓ API compatible changes)
* `eventcore`: 0.7.0 -> 0.7.1 (✓ API compatible changes)
* `eventcore-memory`: 0.7.0 -> 0.7.1 (✓ API compatible changes)
* `eventcore-testing`: 0.7.0 -> 0.7.1 (✓ API compatible changes)
* `eventcore-examples`: 0.7.0 -> 0.7.1

<details><summary><i><b>Changelog</b></i></summary><p>

## `eventcore-macros`

<blockquote>

##
[0.7.0](eventcore-macros-v0.6.0...eventcore-macros-v0.7.0)
- 2026-04-13

### Bug Fixes

- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- enhance require! macro to accept typed error values
([#335](#335))
- add required event_type_name() to Event trait for stable storage
([#344](#344))

### Miscellaneous Tasks

- adopt han plugins, blueprints, and project conventions
([#330](#330))
- consolidate workspace lints and enforce strict lint policy
([#351](#351))

### Refactoring

- expose projection config via free function API, then reduce public
surface ([#357](#357))
</blockquote>

## `eventcore-types`

<blockquote>

##
[0.7.1](eventcore-types-v0.7.0...eventcore-types-v0.7.1)
- 2026-04-15

### Bug Fixes

- filter read_events by event_type to prevent projection stalls
([#373](#373))
</blockquote>

## `eventcore-postgres`

<blockquote>

##
[0.7.1](eventcore-postgres-v0.7.0...eventcore-postgres-v0.7.1)
- 2026-04-15

### Bug Fixes

- filter read_events by event_type to prevent projection stalls
([#373](#373))

### Features

- add load-testing/stress-testing suite
([#370](#370))
</blockquote>

## `eventcore-sqlite`

<blockquote>

##
[0.7.1](eventcore-sqlite-v0.7.0...eventcore-sqlite-v0.7.1)
- 2026-04-15

### Bug Fixes

- filter read_events by event_type to prevent projection stalls
([#373](#373))
</blockquote>

## `eventcore`

<blockquote>

##
[0.7.1](eventcore-v0.7.0...eventcore-v0.7.1)
- 2026-04-15

### Bug Fixes

- filter read_events by event_type to prevent projection stalls
([#373](#373))

### Features

- add load-testing/stress-testing suite
([#370](#370))
</blockquote>

## `eventcore-memory`

<blockquote>

##
[0.7.1](eventcore-memory-v0.7.0...eventcore-memory-v0.7.1)
- 2026-04-15

### Bug Fixes

- filter read_events by event_type to prevent projection stalls
([#373](#373))
</blockquote>

## `eventcore-testing`

<blockquote>

##
[0.7.1](eventcore-testing-v0.7.0...eventcore-testing-v0.7.1)
- 2026-04-15

### Features

- add load-testing/stress-testing suite
([#370](#370))
</blockquote>

## `eventcore-examples`

<blockquote>

##
[0.7.0](eventcore-examples-v0.6.0...eventcore-examples-v0.7.0)
- 2026-04-13

### Bug Fixes

- improve error message consistency, context, and safety across all
crates ([#352](#352))

### Features

- add required event_type_name() to Event trait for stable storage
([#344](#344))
- add TestScenario GWT testing helpers to eventcore-testing
([#346](#346))

### Miscellaneous Tasks

- consolidate workspace lints and enforce strict lint policy
([#351](#351))

### Refactoring

- replace into_inner() with into() for nutype domain types
([#334](#334))
- expose projection config via free function API, then reduce public
surface ([#357](#357))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error Message Consistency Audit

2 participants