pub enum RepairError {
Backend {
op: &'static str,
bucket: String,
key: String,
cause: String,
},
FrameScan {
bucket: String,
key: String,
cause: String,
},
BodyTooLarge {
size: u64,
cap: u64,
},
MissingContentLength {
bucket: String,
key: String,
},
OverwrittenDuringRepair {
bucket: String,
key: String,
head_etag: String,
},
SidecarTooLarge {
bucket: String,
key: String,
size: u64,
cap: u64,
},
NotFramed {
bucket: String,
key: String,
},
EncryptedSidecarUnsupported {
bucket: String,
key: String,
message: String,
},
}Variants§
Backend
FrameScan
BodyTooLarge
MissingContentLength
HEAD on {bucket}/{key} returned no Content-Length header. The
body-size cap that prevents OOM on a runaway repair relies on this
being available, so the tool fails closed rather than treating a
missing length as zero (which would silently bypass the cap).
OverwrittenDuringRepair
If-Match race detector: the object was overwritten between the
initial HEAD (whose ETag we stamped into the sidecar) and the GET.
Returned by repair_sidecar so the operator can re-run instead of
writing a sidecar that’s immediately stale.
SidecarTooLarge
v0.9 #106-audit-R5 P2-R5 (Codex): the <key>.s4index body
the backend reports exceeds MAX_SIDECAR_BODY_BYTES, which
exceeds the codec spec’s max legitimate sidecar (~512 MiB).
Surfaced before the GET to avoid loading a multi-GiB corrupt
or attacker-supplied .s4index blob into the operator’s
repair process (DoS hardening). Operators with anomalously
large legitimate sidecars (multi-million-frame objects) can
raise the cap by changing the constant — but the practical
answer is “treat the underlying object as not-sidecared
(the GET path already falls back to a full read in that
case)” rather than chasing larger sidecars.
NotFramed
v0.9 #106-audit-R3 P2-R3: the object body has no S4F2 frame
magic — it’s a passthrough / raw-bytes object the server
intentionally never sidecared (service.rs::put_object only
builds a sidecar when is_framed && !will_encrypt). Writing
an empty <key>.s4index would silently break Range GET:
FrameIndex::lookup_range over zero entries returns None,
the GET path falls into the “invalid range” branch instead of
the correct passthrough-range fallback that exists for
sidecar-less objects. Surface as a typed error so the
operator knows the object isn’t a candidate for sidecar
repair (and verify-sidecar will already classify it as
MissingHarmless with frame_count=0).
EncryptedSidecarUnsupported
v0.9 #106-audit-R2 P2-INT-1: the object body the backend returned
is an SSE-S4 (S4E1/S4E2/S4E3/S4E4/S4E5/S4E6) encrypted envelope.
repair_sidecar runs against the BACKEND (not the gateway), so the
body it sees is ciphertext — feeding that to the frame scanner
would surface as a confusing FrameScan because the S4F2 frame
magic is hidden inside the encrypted payload. Worse, the v3
sidecar’s sse_v3 binding (key_id / salt / chunk_size etc.)
cannot be reconstructed from the backend bytes alone — it
requires the SSE keyring to decrypt the body and walk the chunk
layout. The CLI does not (yet) accept --sse-s4-key; v0.10
roadmap is to plumb that through. Until then, surface a clean
typed error so the operator can route the repair through a
server-mode rebuild path (re-PUT the object) instead of receiving
a misleading frame-scan failure.
Trait Implementations§
Source§impl Debug for RepairError
impl Debug for RepairError
Source§impl Display for RepairError
impl Display for RepairError
Source§impl Error for RepairError
impl Error for RepairError
1.30.0 · 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 RepairError
impl RefUnwindSafe for RepairError
impl Send for RepairError
impl Sync for RepairError
impl Unpin for RepairError
impl UnsafeUnpin for RepairError
impl UnwindSafe for RepairError
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
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> 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