pub enum DbError {
Show 27 variants
Engine(Error),
Migration {
to: u32,
reason: String,
},
InvalidEdgeType(String),
SingleOpenViolation {
source_id: String,
target_id: String,
edge_type: String,
},
NotFound(String),
DimMismatch {
got: usize,
expected: usize,
model: String,
},
InvalidModelName(String),
ModelNotRegistered {
model: String,
table: String,
},
SubgraphTooLarge {
n: usize,
budget: usize,
},
NegativeEdgeWeight {
source_id: String,
target_id: String,
weight: f64,
},
ReplayCorrupt {
seq: i64,
reason: String,
},
SnapshotIncompatible {
path: String,
reason: String,
},
PayloadVersion {
got: u8,
max: u8,
},
ArchiveViolation {
table: String,
},
AttributeModeUnstated {
as_of: String,
},
DiagnosticConn {
path: String,
reason: String,
},
ArchiveWindow {
window: Duration,
reason: String,
},
InvalidTimestamp {
value: String,
reason: String,
},
InvalidId {
id: String,
reason: String,
},
OverlappingInterval {
overlap: Box<Overlap>,
},
CurrentDrift {
n: usize,
},
RebuildFailed {
n: usize,
},
RebuildInterrupted {
reason: String,
},
WriterUnavailable,
WriterDroppedResponder,
WriterStopped(String),
RecordedAtRegression {
got: String,
had: String,
},
}Expand description
Central error type for the Macrame bitemporal ledger database.
Variants§
Engine(Error)
Migration
InvalidEdgeType(String)
SingleOpenViolation
NotFound(String)
DimMismatch
InvalidModelName(String)
A model name is spliced into DDL and queries as a table identifier, and identifiers cannot be bound as parameters. Validating the name is what makes that splice safe, so an invalid one is refused rather than escaped.
ModelNotRegistered
SubgraphTooLarge
NegativeEdgeWeight
Dijkstra and A* settle a node permanently the first time they pop it,
which is only sound when no later edge can reduce the distance — that is,
when weights are non-negative. links.weight is a bare REAL NOT NULL
with no CHECK, so the guarantee has to be established at load time. The
alternative is a shortest-path result that is quietly just a path.
ReplayCorrupt
SnapshotIncompatible
A snapshot this build cannot read. Distinct from Self::ReplayCorrupt
on purpose: corruption is a fault to report, an incompatible snapshot is
the ordinary consequence of an upgrade, and the correct response is to
discard the file and fold from the log instead (D-043).
PayloadVersion
ArchiveViolation
AttributeModeUnstated
A traversal asked about the past without saying which text it wanted (T3.2, D-085).
TraversalBuilder::as_of(ts) fixes the topology at ts. Node
attributes are a second, independent question, and the default answer —
AttributeMode::Current — is live text. That combination returns the
past’s graph wearing the present’s titles, which is a legitimate thing to
want and a terrible thing to get by accident.
It used to be a tracing::warn!, which is invisible in any application
that has not configured a subscriber. This is the same statement as a
value the caller cannot miss.
Fix by stating the mode: .attribute_mode(AttributeMode::AtTime) for the
past’s text, or .attribute_mode(AttributeMode::Current) to affirm that
live text is what was meant.
DiagnosticConn
crate::Database::diagnostic_conn could not open the file read-only
(T5.1, D-091).
Its own variant rather than NotFound, which renders “node {0} not
found” — naming the wrong subject is the defect D-069 was written to
correct, and a file is not a node.
The case worth the sentence is a missing file:
SQLITE_OPEN_READ_ONLY drops SQLITE_OPEN_CREATE with it, so a path
that does not exist is SQLITE_CANTOPEN rather than a fresh empty
database. That is the right behaviour and an opaque error to receive.
ArchiveWindow
crate::Database::archive_windowed was given a window it cannot use
(T1.1, D-080).
Carries a reason rather than the numbers as fields because the two
cases it covers are not the same shape — a zero-length window never
advances at all, while a merely narrow one produces a session count that
has to be quoted against the limit to mean anything. A caller reading
this needs the sentence, not the struct.
It is an error rather than a silent clamp on purpose. Rounding a one-second window up to something workable would archive over boundaries the caller did not choose, and the caller cannot see that it happened.
InvalidTimestamp
A timestamp that is not in canonical form (§4.1, D-029).
Distinct from Self::ReplayCorrupt, which is what this used to be
(Wave 4.5). timestamp::normalize and timestamp::parse reported bad
caller input as ReplayCorrupt { seq: 0 } — a claim that the ledger is
damaged, carrying a sequence number that cannot exist because
AUTOINCREMENT starts at 1. The same mistake as defect J: an error that
names the wrong subject sends a caller to fix the wrong thing.
The value is reported rather than the provenance, because one function
serves both directions — a caller passing 2026-01-01T00:00:00Z and a
stored recorded_at that will not parse produce the same complaint about
the same string. SystemClock::new is where the second case is
interpreted, and it already logs and floors to the wall clock (D-027).
InvalidId
An identifier the crate’s own encodings cannot represent (D-061).
Distinct from Self::NotFound, and the distinction is defect J: this
id was refused, not looked up. validate_id used to return NotFound
here, which tells a caller the thing is missing and invites them to
create it — with the same id, which will be refused again.
OverlappingInterval
Two valid-time intervals for one relationship claim the same instant.
Distinct from Self::SingleOpenViolation, which is the storage layer’s
guard and covers only the open sentinel. This is the general case, and
it is refused at the API rather than by a trigger (D-060): raw SQL against
the same file can still write an overlap, and §4.2 says so.
The consequence of allowing one is not an error later but a wrong answer:
query_as_of_edges at an instant inside both returns the relationship
twice, and every weighted algorithm downstream double-counts that edge.
Boxed, and it is the only variant that is (D-075). Seven Strings is
168 bytes, which made DbError — and therefore every Result in the
crate, on the Ok path too — larger than clippy::result_large_err’s
threshold the moment D-060 added it. The other variants are well under.
Boxing the rarest one keeps the whole error small rather than trimming
what a caller is told; matches!(err, OverlappingInterval { .. }) is
unaffected, which is how every call site uses it.
CurrentDrift
RebuildFailed
RebuildInterrupted
A chunked shadow rebuild was abandoned rather than committed (T1.2, D-082).
Distinct from Self::RebuildFailed, and the distinction is the whole
point: RebuildFailed means the repair ran and did not repair, which is
a reason to distrust the ledger. This means the repair did not run —
something invalidated the work in progress and it was discarded before it
could be swapped in. links_current is untouched and whatever was true
of it before is still true. The action is to retry.
WriterDroppedResponder
WriterStopped(String)
The actor’s task did not join cleanly at crate::Database::close.
Distinct from Self::WriterUnavailable, which means the channel is
gone while the handle is still in use. This is the shutdown path telling
a caller that the write actor panicked — which close() used to swallow,
so a database whose write path had died closed “successfully” (Wave 4.2).
RecordedAtRegression
Trait Implementations§
Source§impl Error for DbError
impl Error for DbError
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Auto Trait Implementations§
impl !RefUnwindSafe for DbError
impl !UnwindSafe for DbError
impl Freeze for DbError
impl Send for DbError
impl Sync for DbError
impl Unpin for DbError
impl UnsafeUnpin for DbError
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request