ADR-0002: Error Handling Strategy
Status
Accepted
Context
Accounting software needs robust error handling. Errors can occur during:
- Parsing (syntax errors, invalid dates, malformed amounts)
- Loading (file not found, include cycles, path traversal)
- Validation (unbalanced transactions, unknown accounts)
- Query execution (type mismatches, unknown columns)
We need to decide:
- How to represent errors (enums, traits, strings)
- Whether to fail-fast or collect multiple errors
- How to provide good error messages with source locations
Decision
Error Representation
Use thiserror to derive Error implementations with strongly-typed error enums per crate:
ParseErrorin rustledger-parserLoadErrorin rustledger-loaderValidationErrorin rustledger-validateQueryErrorin rustledger-query
Each error type includes relevant context (spans, file paths, expected vs found values).
Error Collection vs Fail-Fast
Parser: Collect errors and continue parsing to report multiple issues at once. Return ParseResult with both directives and errors vectors.
Loader: Collect errors for parse failures and path issues, but fail-fast on include cycles (which would cause infinite loops).
Validator: Collect all validation errors to report everything wrong with a ledger in one pass.
Query: Fail-fast on query errors since partial results would be misleading.
Source Locations
All errors that can occur at specific source locations include Span information (byte offsets). The SourceMap tracks file contents for error rendering.
Consequences
Positive
- Users see all syntax errors at once, not one at a time
- Typed errors enable programmatic handling
- Span information enables IDE-quality error messages
#[must_use]on Result types prevents ignored errors
Negative
- Error collection requires more complex parser recovery logic
- Multiple error types mean more code to write
- Must be careful to propagate errors correctly
Neutral
- Using
thiserrorrather than manualimpl Error