#[non_exhaustive]pub enum Error {
Show 44 variants
Io(Error),
Decompress(CompressionType),
InvalidVersion(u8),
UnsupportedFormat {
part: FormatPart,
found: Option<u8>,
expected: u8,
},
Unrecoverable,
Cancelled,
Excised {
offset: u64,
},
ChecksumMismatch {
got: Checksum,
expected: Checksum,
},
MemtableKvChecksumMismatch {
seqno: u64,
got: u64,
expected: u64,
},
MemtableKvChecksumCorruptAlgorithm {
seqno: u64,
tag: u8,
},
RepairedButUnopened {
report: Box<RepairReport>,
cause: Box<Self>,
},
TreeTypeMismatch {
requested: TreeType,
actual: TreeType,
},
HeaderCrcMismatch {
recomputed: u32,
stored: u32,
},
InvalidTag((&'static str, u8)),
InvalidTrailer,
InvalidHeader(&'static str),
DecompressedSizeTooLarge {
declared: u64,
limit: u64,
},
Utf8(Utf8Error),
MergeOperator,
Encrypt(&'static str),
Decrypt(&'static str),
ComparatorMismatch {
stored: String,
supplied: &'static str,
},
ZstdDictMismatch {
expected: u32,
got: Option<u32>,
},
ZstdDictCorrupt {
id: u32,
got: Option<u32>,
},
ManifestFrameChecksumMismatch {
section: &'static str,
expected: u64,
got: u64,
},
RangeTombstoneDecode {
field: &'static str,
offset: u64,
},
MixedOperationBatch,
PageEccUnsupported,
PageEccUnrecoverable {
got: Checksum,
expected: Checksum,
},
RouteMismatch {
expected: usize,
found: usize,
},
FeatureUnsupported(&'static str),
Locked(String),
ManifestFooterInvalid(&'static str),
ManifestSectionInvalid(&'static str),
TornManifestEditLog {
kind: &'static str,
},
StorageFull {
used: u64,
limit: u64,
},
SnapshotBelowRetention {
requested: SeqNo,
oldest_retained: SeqNo,
},
RetentionFloorRaise {
current: SeqNo,
requested: SeqNo,
},
RetentionFloorAboveSeqno {
seqno: SeqNo,
requested: SeqNo,
},
Projection(&'static str),
BlobRef(&'static str),
CellRow(&'static str),
SalvageOutput(Box<Self>),
InstallOutcomeUnknown(Box<Self>),
}Expand description
Represents errors that can occur in the LSM-tree
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Io(Error)
I/O error
Decompress(CompressionType)
Decompression failed
InvalidVersion(u8)
The store declares an on-disk format version this release does not
read. The data is not damaged; see the Display form for the remedy.
UnsupportedFormat
A table carries a part in an on-disk format this release does not
read: its BuRR filter and locator sections, or its columnar row
groups.
6.0 replaced both outright. The filter’s solution matrix is packed to
r bits per row instead of a full 64-bit word per row; a columnar row
group is a page directory and independently readable pages instead of
one block. No reader for either old layout ships, deliberately:
keeping one would put a legacy decode path on the read hot path for as
long as any cold level went un-rewritten.
The store is intact and its data is not lost — the table has to be
rewritten by the offline converter before this release can open it.
Reported when the table is opened rather than from inside the first
read, so an operator learns what to do instead of seeing a parse fail
somewhere under a get.
On the converter not being in the tree yet. The gate and the converter are separate deliverables that block the same release, so no published build ever refuses a store it has no tool for. Keeping a migration-capable reader in the engine until the tool lands was considered and rejected: a legacy decode path on the read hot path would outlive the migration by however long the coldest level goes un-rewritten, which for a bottom level can be indefinitely. The engine carries one format; conversion is a one-way offline step.
Fields
part: FormatPartWhich part of the table is in the unsupported format.
Unrecoverable
Some required files could not be recovered from disk
Cancelled
The operation was aborted on a caller’s cooperative cancellation
request (RecoveryProgress::request_cancel).
A repair cancelled BEFORE its manifest commit left the directory untouched (the scan is read-only), so a retry re-derives everything from the same bytes; a cancel requested after the commit is ignored rather than reported, since the rebuilt manifest is already durable.
Excised
The read resolved into an extent that was physically EXCISED — a hole-punched region that reads back as zeros.
Tight-space reclaim punches consumed extents in place, and manifest recovery may keep a table whose surviving blocks are intact around such a hole rather than discard the whole file. Reading one of the punched blocks is a genuine, permanent loss of exactly those rows, and it is reported as such instead of being smuggled out as a checksum mismatch (which reads as “the bytes rotted”) or, worse, as “no such key” — the latter would fall through to a superseded version in a lower level and silently resurrect it.
This is the engine’s equivalent of a filesystem returning EIO for a
bad extent while the rest of the file keeps reading.
ChecksumMismatch
Checksum mismatch
Fields
got: ChecksumChecksum of loaded block
expected: ChecksumChecksum that was saved in block header
MemtableKvChecksumMismatch
A memtable entry’s per-KV digest, computed at insert under
KvChecksumComputePoint::AtInsert,
did not match a recompute over the entry’s current bytes at flush.
This is the memtable-residence RAM-corruption signal: the entry’s
logical content (value_type, seqno, key, or value) changed while it
sat in the memtable, between insert and flush. Distinct from
Self::ChecksumMismatch (on-disk block bytes) — this catches a flip
that happens entirely in RAM, before any block is written.
Fields
MemtableKvChecksumCorruptAlgorithm
A memtable entry carried an insert-time per-KV digest
(KvChecksumComputePoint::AtInsert)
tagged with an algorithm AtInsert never stores: a non-4-byte or
unknown algorithm wire tag.
AtInsert only ever writes a 4-byte algorithm tag (Xxh3Low32 /
Crc32c), so a digest-bearing node tagged otherwise means the node’s
algorithm metadata was corrupted in RAM during memtable residence.
Distinct from Self::MemtableKvChecksumMismatch (the digest value
diverged): here the algorithm itself is unusable, so the engine refuses
to “verify” the entry under the wrong algorithm rather than risk a
flipped tag passing the residence check.
Fields
RepairedButUnopened
A repair COMMITTED its rebuilt manifest, but a later step failed: the
repair’s own post-commit cleanup, or the follow-up open inside
Config::open_or_repair.
The report travels WITH the error, so an external-WAL consumer learns
its replay obligation
(RepairReport::wal_replay_scope)
whatever the retry finds. Consume the report exactly as a successful
repair’s, then retry the open (cause is typically transient). The
snapshot keeps owing the report until it is acknowledged, so the retry
hands it out again with the same
RepairReport::id: skip one already applied.
Fields
report: Box<RepairReport>The completed repair’s report — not rederivable on a retry.
TreeTypeMismatch
The on-disk tree’s type does not match the type the configuration
requests: a standard open (no kv_separation_opts) of a KV-separated
(blob) tree, or the reverse.
This is a CONFIGURATION error — fix the options and reopen — not
damage, so Config::open_or_repair
propagates it instead of repairing: a rebuild under the mismatched
type would commit a manifest of the wrong tree shape (a Standard
rebuild of a blob tree strands every blob file behind SSTs full of
indirections, and the orphan sweep then deletes them).
Fields
HeaderCrcMismatch
Blob frame header CRC mismatch (V4 format).
Distinct from ChecksumMismatch which covers data payload checksums.
Fields
InvalidTag((&'static str, u8))
Invalid enum tag
InvalidTrailer
Invalid block trailer
InvalidHeader(&'static str)
Invalid block header
DecompressedSizeTooLarge
Data size (decompressed, on-disk, or requested) is invalid or exceeds a safety limit
Fields
Utf8(Utf8Error)
UTF-8 error
MergeOperator
Merge operator failed.
No context payload — consistent with other unit variants
(Unrecoverable, InvalidTrailer). Operators should log
details before returning this error.
Encrypt(&'static str)
Encryption failed
Decrypt(&'static str)
Decryption failed
ComparatorMismatch
Comparator mismatch on tree reopen.
The tree was created with a comparator whose crate::UserComparator::name
differs from the one supplied at reopen time.
Fields
ZstdDictMismatch
Zstd dictionary required but not provided, or dict_id mismatch
Fields
ZstdDictCorrupt
A dictionary the tree stores no longer hashes to the id it is filed under, or on an encrypted tree its seal no longer opens: its bytes were altered or truncated on disk.
Damage rather than configuration, unlike Self::ZstdDictMismatch: the
id is the truncated hash of the content, so the file disagreeing with its
own name is decided by the bytes alone. A repair moves such a file aside
(see RepairReport::damaged_dictionaries),
and Config::open_or_repair engages one.
Fields
ManifestFrameChecksumMismatch
Per-record XXH3-64 mismatch inside a framed manifest section
(tables / blob_files). Distinct from
Error::ChecksumMismatch — same XXH3 family but a
different output width (XXH3-64 here vs XXH3-128 for
block-level payloads) on a different layer of the on-disk
format. Raised in every recovery mode, so an operator sees the
exact 64-bit digests that disagreed; the remedy is
Config::repair.
Fields
RangeTombstoneDecode
Range tombstone block decode failure.
Fields
MixedOperationBatch
A WriteBatch contains mixed operation types
(e.g. insert + remove) for the same user key.
Mixed ops at the same logical version are rejected because the
memtable/skiplist ordering ties on (user_key, seqno) and does not
include value_type as a tie-breaker. That would otherwise make
equal-key entries with different operation types ambiguous to later
reads and merges, yielding tie-break-dependent “last write wins”
semantics.
PageEccUnsupported
Tree was opened with Config::page_ecc(true) but this build of
the crate does not have the page_ecc cargo feature enabled.
The reader has no way to verify or recover Reed-Solomon parity
without the codec, so opening such a tree would silently
downgrade integrity guarantees — return this error instead.
PageEccUnrecoverable
Block payload failed the XXH3 integrity check and the
attached Reed-Solomon parity trailer could not reconstruct
it (more shards are corrupted than the (4, 2) RS scheme
can recover). Surfaced ONLY by ECC-protected blocks
(the ECC_PARITY header flag set); a block written without parity
(Config::page_ecc(false)) on a checksum mismatch returns
Self::ChecksumMismatch instead, because there’s no
parity to even attempt recovery from.
Fields
got: ChecksumXXH3 checksum recomputed from the on-disk bytes.
expected: ChecksumXXH3 checksum stored in the block header.
RouteMismatch
Route-compatibility mismatch on reopen.
Recovery found fewer tables on disk than the manifest expects, and all
missing tables are on levels not covered by any current
level_routes. This typically means a
previously configured route was removed, leaving its directory
unreachable.
Re-adding the missing route(s) will usually resolve the error. If
missing tables are on levels that are covered by a current route,
recovery returns Unrecoverable instead
(the SST files were genuinely lost).
Fields
FeatureUnsupported(&'static str)
Valid configuration / on-disk layout that this build does not
yet know how to process, or constructor input that violates a
documented invariant (e.g. CompressionType::None passed to
[crate::table::block::CompressionContext::new]). Distinct
from Error::Unrecoverable (signals corruption) and from
Error::Io with ErrorKind::Unsupported (which can also
surface from platform / backend limits); the &'static str
payload names the specific marker that triggered the rejection
(e.g. "filter_tli" for a partitioned filter SFA section,
"compression-context-none" for the constructor invariant) so
the caller can route the diagnostic without parsing message
strings.
Locked(String)
The tree directory is already locked by another live instance.
Returned by Config::open and
Config::repair when the cross-process
directory lock (a LOCK file under the tree directory, held via an
advisory OS file lock) could not be acquired because another process
owns it. Holds the directory path as a display string for diagnostics.
Two processes mutating the same manifest would corrupt it, so the second
acquirer fails fast here. Disable the lock with
Config::with_directory_lock only
when exclusivity is enforced at a higher layer.
Manifest footer / TOC / file-level discovery failure.
Scoped to errors detected at or before the TOC is parsed —
i.e. everything the reader needs before it can answer
“where is section X”. Section-content failures (a specific
section’s Block fails verification) go through
ManifestSectionInvalid
instead so callers like Tree::open can distinguish a
totally unreadable manifest from a per-section problem.
Typical causes:
- Footer-payload structural failure: unknown layout version, oversized section count, empty/oversized section name, invalid UTF-8, duplicate section name, footer payload exceeds the 4 KiB reservation.
- Tail / head-mirror double failure: both the
tail-footer Block read and the head-mirror fallback
failed verification (XXH3 mismatch, AEAD decryption,
parse error). Per-path causes are logged at
errorlevel and collapsed here. - TOC entry value corruption: a TOC entry’s
block_offset + block_sizeoverflowsu64or extends past the end of the file. The TOC bytes are footer payload, so a malformed TOC entry is a footer-level issue even though it surfaces inManifestArchiveReader::read_section. - Trailing size-hint corruption: the tail’s 4-byte
footer-size hint is zero or exceeds
HEAD_FOOTER_RESERVED_SIZE(4 KiB), or the impliedsection_endlands inside the head reservation. - Writer-side invariant breach:
write_cursorwould overflowu64, an in-memory section would exceed the on-disk Block-size cap, etc. - No installed snapshot to recover: recovery found no
v{N}in the tree folder (a missingedits-{N}log is read as empty), soTree::open’s outerIo(NotFound) => create_newarm cannot mistake a half-applied recovery / corrupted state for a clean first-open. - Installed snapshot naming another id: the snapshot’s
own
version_idsection differs from the id in its name.
ManifestSectionInvalid(&'static str)
Manifest section content failed verification or matched no TOC entry.
Surfaced by ManifestArchiveReader::read_section (and the
helper that validates the inner Block header before
delegating to Block::from_reader). Distinct from
ManifestFooterInvalid
because the footer / TOC loaded fine — the bad bytes are
localised to one section Block and a caller MAY route
recovery differently (e.g. skip the section vs. refuse
the whole manifest).
Causes:
- Requested section name not in TOC: the caller asked for a section that the manifest doesn’t declare.
- Section Block header doesn’t fit its outer buffer:
the inner block’s derived on-disk size (header + payload +
parity-if-flagged) exceeds the TOC-declared
block_size. Defence-in-depth against a forged TOC pointing at a too-small slot. - Block decoded at the TOC offset has the wrong
block_type: TOC says “section here” but the bytes carry a non-ManifestBlock. Defence-in-depth against TOC-redirect attacks; once AAD-binding lands inencryption::block,Block::from_readerwill reject this internally and the check here becomes belt-and- braces.
TornManifestEditLog
The trailing record of the incremental manifest edit log is
incomplete or corrupt, and the active
ManifestRecoveryMode does
not tolerate that defect, so the open aborts rather than silently
rolling the edit back.
A clean end-of-log is never reported here: a crash exactly at a
record boundary is byte-identical to a pristine close, so that
case is always tolerated. This fires when bytes of a trailing
record are present but the record fails framing: a
power-loss-truncated append (rejected by
AbsoluteConsistency,
rolled back by
TolerateCorruptedTailRecords),
or a fully-framed record whose checksum doesn’t match (bit-rot) /
whose header is forged, which every mode rejects.
Recover by running Config::repair, which
rebuilds a clean standalone snapshot from the table files (dropping
the edit log). A torn tail alone can instead be rolled back by
re-opening under TolerateCorruptedTailRecords.
Fields
kind: &'static strThe trailing defect detected: "truncated" (partial record
from a power-loss-interrupted append), "checksum-mismatch"
(fully-framed record whose payload bit-rotted),
"bad-header" (implausible framing length), or
"len-mismatch" (record length disagrees with the expected
fixed size). Static so callers can branch without parsing
the message string.
StorageFull
A write was declined by the storage admission gate because accepting it could push the tree’s live footprint past its effective budget.
Only produced when storage_admission_check
is enabled. The predicate is computed, not latched: raising
storage_limit_bytes,
freeing disk, or a compaction reclaiming space clears the read-only
state on the next check with no restart. Internal flush / compaction are
never gated (reserved headroom), so the engine can always reclaim space.
Fields
SnapshotBelowRetention
A read asked for a snapshot at or below the retention floor.
Every snapshot above the floor is served from the current version. An
install that loses or rewrites what an older snapshot read raises the
floor past it: a compaction or flush that collected history below the
caller’s GC watermark
(AbstractTree::major_compact’s
gc_watermark), a filtering compaction, a table drop,
AbstractTree::clear. Serving such a
snapshot from what survived would silently answer with data it never
saw, so the read is refused. Snapshot 0 is the exception: it sees no
entry from any version and is always served (empty). The floor is
persisted with the manifest, so the refusal is the same on either side
of a reopen. A reader that resolved its version before the install is
not affected: it keeps reading that version until it drops it.
Point reads return it directly; iterators yield it as their first and
only item. AbstractTree::oldest_retained_seqno
exposes the boundary so a caller can validate a snapshot before
reading: a snapshot is servable iff it is 0 or strictly above that
seqno.
Fields
RetentionFloorRaise
AbstractTree::lower_retention_floor
was asked for a floor above the current one. Only GC and the
installs that drop data raise the floor; the request changed nothing.
RetentionFloorAboveSeqno
AbstractTree::lower_retention_floor
was asked for a floor above the tree’s seqno counter: it would refuse
snapshots the counter has yet to hand out. The request changed nothing.
Projection(&'static str)
A projected columnar scan cannot produce a field as its projection declares: the field is absent from a row and declared an error, a segment stores it under another type, a row value has to be read and no projector is set, or the projector wrote a cell that does not fit.
A caller-side mismatch between the projection and the data, not damage. The payload names which of these it is.
BlobRef(&'static str)
A blob reference of a row written as cells cannot be used: a
BlobRef read from another
key or another tree, one object in two cells of the row, a reference in
an ingested row, or a stale reference, whose object was moved or
released since it was read; or a reference-aware read finds the key’s
version is not written as cells.
A caller-side error, refused before anything is written. The payload names which of these it is; only a stale reference is cured by reading the key again.
CellRow(&'static str)
A row written as cells is not one the caller could have meant: two fields in one column, or a fixed-width field that is not its type’s width.
A caller-side error, refused before anything is written. The payload names which of these it is.
SalvageOutput(Box<Self>)
A salvage could not write, finish or make durable its OUTPUT: the failure lies with the destination, not with the bytes of the file being salvaged.
A recovery that met it stops rather than grade the SOURCE unreadable: that would leave intact data out of the rebuilt manifest, and the cleanup after the commit would remove it. Fix the destination (its space, its device) and retry.
InstallOutcomeUnknown(Box<Self>)
A manifest install failed after its edit or snapshot was written, so a power loss may still make it the installed one: what it names is kept on disk, and the next install writes a full snapshot over it.
The operation that asked for the install (a flush, a compaction) reports it as failed and may be retried; the tree reopened after a crash holds either the state before it or the state it installed, whole. The payload is the failure itself.
From a repair, the report rides in the snapshot it describes: when that
snapshot turns out installed,
Config::open_or_repair hands the
report out; when it does not, its own repair answers with a fresh one.
Trait Implementations§
Source§impl Error for Error
impl Error for Error
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 Freeze for Error
impl RefUnwindSafe for Error
impl Send for Error
impl Sync for Error
impl Unpin for Error
impl UnsafeUnpin for Error
impl UnwindSafe for Error
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> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more