#[non_exhaustive]pub enum ContentError {
EncodingError {
source: Box<dyn Error + Send + Sync + 'static>,
},
DecodingError {
source: Box<dyn Error + Send + Sync + 'static>,
},
VerificationFailed {
expected: String,
computed: String,
},
NonCanonical,
LossyDecode,
InvalidCid {
reason: String,
source: Box<dyn Error + Send + Sync + 'static>,
},
InvalidCidProfile {
reason: String,
},
}Expand description
The error type returned by all fallible operations in this crate.
Variants are intentionally coarse: they describe which stage failed
(serialize, deserialize, verify, non-canonical, parse) rather than mirroring
every underlying library error one-to-one. Where a lower-level error is
available it is preserved in the source field for diagnostics.
§Stability (FROZEN at 0.1.0)
This enum is #[non_exhaustive] as a frozen decision: it lets a new
variant be added in a future 0.1.x release without a major version bump,
while the seven names and their fields below are a stability contract.
(Six were frozen at 0.1.0; LossyDecode was
added in 0.1.2 through exactly that additive path.) See the module docs for the full freeze rationale (no
#[from], boxed codec sources, the operation→variant map) and a compile-time
Send + Sync + 'static guard locking ContentError’s thread-portability.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
EncodingError
Canonical dag-cbor encoding of a value failed.
Returned by
to_canonical_dagcbor (and so
propagated by content_id /
verify via canonical_form). The
source is boxed as a dyn Error so the concrete
serde_ipld_dagcbor::EncodeError<…> generic does not leak into this
frozen signature.
DecodingError
Decoding dag-cbor bytes back into a value failed.
Returned by every decode path in the crate, checked or not:
from_canonical_dagcbor (the
deprecated unchecked door),
from_canonical_dagcbor_checked,
ContentAddressable::from_canonical_form,
and from_canonical_bytes_checked.
It covers two distinct situations, and the checked doors reach both: the
bytes are not dag-cbor at all, or they are dag-cbor but do not decode as
the target type. For a type whose canonical_form is deliberately not its
serde representation, the second is the expected answer for its own
canonical bytes — see
from_canonical_form.
The source is boxed as a dyn Error so the concrete
serde_ipld_dagcbor::DecodeError<…> generic does not leak into this
frozen signature.
VerificationFailed
A crate::ContentId did not match the expected one.
This is the heart of the doctrine: the data did not carry the proof it claimed to. The recomputed identity differs from the expected identity.
§Frozen verify ruling (0.1.0)
verify returns Ok(false) on a
mismatch — a mismatch is not an Err (a negative answer to “do these
match?” is a successful, expected result, not a failure to check). This
is a frozen part of the 0.1.0 API surface (README gate item #8).
This variant is the escalation of that boolean: it is constructed by
ensure_content_id, the
strict helper that turns a mismatch into an Err for ?-propagation.
Its expected / computed fields are the two ids’ Display strings
(multibase base32-lower b…).
Fields
NonCanonical
Bytes presented as canonical dag-cbor were valid CBOR but not canonical dag-cbor.
Returned by every door that verifies canonicality, all of which run one
shared generic gate:
from_canonical_bytes_checked,
from_canonical_dagcbor_checked,
and
ContentAddressable::from_canonical_form
— plus the unstable-store seam’s strict ingest and identity-preserving
read, which are those doors under other names.
It means the input decoded as an Ipld value but
re-encoding it did not reproduce the input bytes (wrong map-key order,
indefinite-length items, non-smallest integers, …). No target type is
involved: the verdict is about the bytes alone, which is why it is
established before any type is constructed. The fast
from_canonical_bytes primitive
never produces this — it trusts its precondition and only hashes.
LossyDecode
A typed round-trip mismatch: the value decoded, but re-encoding it does not reproduce the input bytes.
Produced at the third stage of the two checked doors —
canonical::from_canonical_dagcbor_checked,
which re-encodes with
to_canonical_dagcbor, and
ContentAddressable::from_canonical_form,
which re-encodes with the type’s own
canonical_form. Those two
are the only crate-owned stage-three construction sites — a user’s own
canonical_form may of course
return this public variant, and when it does the crate propagates it
verbatim rather than reinterpreting it (see below).
The bytes were already proven canonical by the time this can fire, so they are not at fault: the mismatch is between the input and the target type’s canonical representation of what it decoded.
Information loss is the usual cause and the one that motivated the
variant: serde ignores unknown map keys by default, so a record carrying
a field the type does not name decodes cleanly with the field gone, and
for a protocol record that field may have been a required demand.
#[serde(default)] and aliases land in the same place. It is not the
only cause, though, and a LossyDecode is not by itself proof that a
field was dropped: a type whose canonical_form intentionally differs
from its serde representation can decode faithfully and still re-encode to
different bytes. Either way the conclusion the caller needs is the same —
these bytes are not the canonical form of this value — which is why one
variant covers both, and why the display message speaks of the mismatch
rather than naming a missing field.
Distinct from NonCanonical on purpose:
NonCanonical blames the bytes (re-encode them), LossyDecode blames the
bytes/type pairing (widen the type, name every field, or give the custom
canonical form the custom decoder it needs). Like NonCanonical it is a
policy rejection and carries no underlying source — nothing failed
underneath; the comparison simply came out unequal.
Two things it is not. It is not what a user’s own canonical_form
returning an error produces — that error propagates verbatim, whatever it
is. And under the unstable-store seam it is not what a mismatched typed
read reports: NodeStoreExt::get_node returns its own
StoreError::RepresentationMismatch, which names the id that lied, so a
LossyDecode surfacing from that seam came from the user’s encoder.
Added in 0.1.2 (issue #90) under the enum’s #[non_exhaustive]
contract.
InvalidCid
A CID could not be parsed from a string or from bytes.
Returned by ContentId::from_bytes and
the FromStr impl. reason is a stable
human-readable string; the underlying cid::Error is preserved in
source so the error chain (.source()) is not stripped at this
boundary.
Fields
InvalidCidProfile
A syntactically-valid CID was parsed, but it is not this crate’s frozen
profile: CIDv1 + dag-cbor (0x71) + BLAKE3 (0x1e) + a 32-byte digest.
Every ingress path — from_bytes, the
FromStr impl, TryFrom<Cid>, and
the binary/IPLD Deserialize — rejects a foreign CID with this, so every
ContentId (however it entered) carries the fixed profile the presentation
accessors (digest_bytes etc.) rely on.
A policy rejection, so — like NonCanonical —
it carries no underlying source; reason names the parameter that was off.
Trait Implementations§
Source§impl Debug for ContentError
impl Debug for ContentError
Source§impl Display for ContentError
impl Display for ContentError
Source§impl Error for ContentError
impl Error for ContentError
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()