Skip to main content

DurableError

Enum DurableError 

Source
#[non_exhaustive]
pub enum DurableError {
Show 19 variants ReplayDivergence { step_id: StepId, }, AmbiguityPolicyRequired { step: &'static str, }, JournalUnavailable, PayloadTooLarge { size: u64, max: u64, }, Decode { context: &'static str, }, ReplayIntegrity, ControlIntegrity, StepCapExceeded { cap: u32, }, EncryptionRequired { context: &'static str, }, UnsupportedEntryKind { kind: &'static str, }, Storage { op: &'static str, source: Box<dyn Error + Send + Sync>, }, StepFailed { step: &'static str, source: Box<dyn Error + Send + Sync>, }, AmbiguousEffect { step_id: StepId, }, Serialize { step: &'static str, }, UnknownPromise, PromiseRejected, HighWaterMarkIntegrity { execution_id: ExecutionId, reason: &'static str, hint: &'static str, }, ExecutionLocked { execution_id: ExecutionId, holder_pid: u32, }, ExecutionCanceled { execution_id: ExecutionId, },
}
Expand description

An error raised by the durable execution layer.

The enum is #[non_exhaustive]: follow-up issues add variants as runtime behavior lands, and downstream match expressions must keep a wildcard arm.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

ReplayDivergence

The replayed step’s descriptor fingerprint did not match the fingerprint journaled for this StepId (INV-3). The execution is discarded and restarted fresh rather than returning a result for a structurally different step.

Fields

§step_id: StepId

The step whose fingerprint diverged.

§

AmbiguityPolicyRequired

A destructive or security-relevant ExactlyOnceGuarded step was constructed without an explicit ambiguity policy. The safety decision must be made at the call site, not deferred to a runtime default.

Fields

§step: &'static str

The name of the offending step descriptor.

§

JournalUnavailable

The journal writer did not acknowledge an append within the configured timeout, or is otherwise unreachable. The calling path degrades to non-durable mode rather than hanging (INV-12).

§

PayloadTooLarge

A payload exceeded the configured max_payload_bytes limit. Enforced on both append and read; it fails closed and never panics (INV-11).

Fields

§size: u64

The size of the offending payload, in bytes.

§max: u64

The configured maximum payload size, in bytes.

§

Decode

A journal entry could not be decoded: corrupt, truncated, or written under an unknown wire format version. Fails closed.

Fields

§context: &'static str

A non-sensitive description of the decode failure.

§

ReplayIntegrity

AEAD authentication failed when opening a sealed payload: the entry was forged, moved to a different step, or replayed under a different execution. Fails closed.

§

ControlIntegrity

A control entry’s row-level HMAC (INV-8) did not verify against a recomputed value: the row was forged, relocated to a different step/execution, or is missing its HMAC even though the backend is keyed. Fails closed like ReplayIntegrity, but for HMAC-authenticated control entries (EffectIntent) rather than AEAD-sealed payloads.

§

StepCapExceeded

An execution exceeded the hard per-execution step cap and was aborted rather than allowed to grow unboundedly.

Fields

§cap: u32

The configured hard step cap.

§

EncryptionRequired

AEAD payload encryption was disabled (encrypt_payload = false) for a deployment where it is mandatory — a non-local backend or a shared database (INV-8). The DB-file trust boundary does not hold in multi-client environments, so this fails closed at startup.

Fields

§context: &'static str

A non-sensitive label for the deployment that mandates encryption (e.g. "restate" or "shared-database").

§

UnsupportedEntryKind

A journal entry of a kind whose persistence is provided by a higher layer not yet wired into this backend revision. Promise, timer, and checkpoint entries land with the promise/timer and retention layers; until then the backend fails closed rather than silently dropping the entry’s kind-specific state.

Fields

§kind: &'static str

The entry_kind tag of the entry whose persistence is deferred.

§

Storage

A journal storage operation failed at the database layer (connection, migration, or query).

The static op names the failing operation; the underlying database error is attached as the error source. Per INV-5 the Display message carries only the operation name — the boxed source never contains plaintext payloads, since every bind is ciphertext, a hash, or a non-secret descriptor.

Fields

§op: &'static str

The static name of the failing operation (e.g. "append", "finalize", "open").

§source: Box<dyn Error + Send + Sync>

The underlying database error.

§

StepFailed

A step’s operation closure returned an error on a fresh execution. The step did not complete, so no StepResult is journaled; on a later resume the step re-runs (or, for a guarded effect, its OnAmbiguous policy applies). The closure’s own error is attached as the source.

Fields

§step: &'static str

The name of the step whose operation closure failed.

§source: Box<dyn Error + Send + Sync>

The closure’s underlying error.

§

AmbiguousEffect

A guarded step resumed inside the ambiguous window (an EffectIntent is journaled but no StepResult) and its policy is OnAmbiguous::Fail: the layer refuses to guess whether the irreversible effect fired and surfaces the decision to the operator instead of re-running or skipping it.

Fields

§step_id: StepId

The step caught in the ambiguous window.

§

Serialize

A step result could not be serialized into journal bytes before sealing. The step’s value is the consumer’s serializable type, so this indicates a faulty Serialize implementation; it fails closed rather than journaling a partial payload. Per INV-5 only the step name is named.

Fields

§step: &'static str

The name of the step whose result failed to serialize.

§

UnknownPromise

A promise resolution referenced a promise that has no durable_promises row — either never created, or pruned. Fails closed rather than silently succeeding. Per INV-5 the raw PromiseId is semi-sensitive and is therefore not embedded in the message.

§

PromiseRejected

A promise resolution presented a resolver token that did not match the stored hash (INV-9). The comparison is constant-time, and neither the presented token nor the raw PromiseId appears in the message (INV-5). The pending promise is left untouched.

§

HighWaterMarkIntegrity

The execution’s authenticated high-water-mark (issue #6360) did not verify on resume.

The high-water-mark is a signed {key_epoch, max_committed_step_id, committed_result_count} tuple, recomputed on every resume from the surviving StepResult rows plus every checkpoint’s persisted folded_count and compared against the value signed at write time. Unlike ControlIntegrity (a single-row check), this is a whole-execution fail-closed abort (FR-004, US-003): a mismatch means a committed result was deleted, or the signed tuple itself was tampered with outside the write path. The durable resume path never offers an override for this variant — it always hard-aborts.

Fields

§execution_id: ExecutionId

The execution whose high-water-mark did not verify.

§reason: &'static str

A stable, non-sensitive, machine-matchable classification of the failure (INV-5): "count_mismatch" (the recomputed committed-result count disagreed with the signed value), "hmac_mismatch" (the signed tuple’s HMAC did not authenticate under the current epoch’s key), or "key_epoch_unresolvable" (the stored key_epoch is neither the current nor a known previous rotation epoch, per FR-008/NFR-004 — a chained/HWM- bearing entry with an unresolvable key always fails closed rather than degrading to legacy).

§hint: &'static str

A human-readable operator hint distinguishing “possibly re-keyed” (a legitimate key rotation the durable resume path cannot resolve automatically) from “TAMPER” (the content itself did not authenticate), per FR-008 — so an operator reading logs is not misled into treating a rotation-window miss the same as a confirmed forgery. Durable resume never offers an interactive override for either case (FR-004): the hint informs the operator’s own follow-up action, it does not unlock a bypass.

§

ExecutionLocked

crate::backend::LocalBackend::open_execution_exclusive found another process already holding the execution’s advisory lock (INV-15, #6122).

Two processes deriving the same ExecutionId (e.g. two CLI instances pointed at the same memory.sqlite_path and the same ConversationId) can no longer both drive it concurrently: the second process gets this error instead of silently racing the first into ReplayDivergence/ReplayIntegrity failures. Distinct from those two variants so callers (and operators reading logs) can tell “another live process owns this execution” apart from “the journal itself is corrupt or was tampered with”.

Fields

§execution_id: ExecutionId

The execution whose lock is already held.

§holder_pid: u32

PID of the process currently holding the lock, or 0 if it could not be determined.

§

ExecutionCanceled

crate::backend::LocalBackend::open_execution (or its exclusive variant) found the execution’s row already canceled (INV-16′, #6362). Unlike completed/failed/aborted, a canceled row is never un-finalized and reopened — the cancellation was an explicit operator decision that this execution must not run again.

Fields

§execution_id: ExecutionId

The execution whose row is canceled.

Trait Implementations§

Source§

impl Debug for DurableError

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for DurableError

Source§

fn fmt(&self, __formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Error for DurableError

Source§

fn source(&self) -> Option<&(dyn Error + 'static)>

Returns the lower-level source of this error, if any. Read more
1.0.0 · Source§

fn description(&self) -> &str

👎Deprecated since 1.42.0:

use the Display impl or to_string()

1.0.0 · Source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0:

replaced by Error::source, which can support downcasting

Source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type-based access to context intended for error reports. Read more
Source§

impl From<CipherError> for DurableError

Source§

fn from(err: CipherError) -> Self

Lift a cipher failure into the crate-wide error, preserving fail-closed semantics.

An authentication failure is a replay-integrity violation; a structural or key-selection failure is a decode failure. Both fail closed — no plaintext is ever returned.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more