1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//! `StoreError` taxonomy.
use aion_core::WorkflowId;
/// Errors returned by [`crate::ReadableEventStore`] and [`crate::EventStore`] implementations.
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum StoreError {
/// The workflow history head did not match the caller's optimistic-concurrency guard.
#[error(
"sequence conflict (double-writer bug indicator): expected workflow head {expected}, found {found}"
)]
SequenceConflict {
/// Sequence number the caller expected to be the current workflow head.
expected: u64,
/// Sequence number currently stored as the workflow head.
found: u64,
},
/// Reserved for operations that target a must-exist workflow; read and query methods return
/// empty results, not `NotFound`, for absent workflows.
#[error("workflow {workflow_id} was not found")]
NotFound {
/// Workflow identifier targeted by the must-exist operation.
workflow_id: WorkflowId,
},
/// An assistant-session transcript append named a session with no record.
///
/// Deliberately NOT a silent create: a transcript with no session record is
/// a conversation with no owner, no harness and no subject, which every
/// read surface would then have to invent an answer for. Shaped like
/// [`Self::NotFound`] — the must-exist refusal — but carrying a session id
/// rather than a workflow id, because an assistant session is not a workflow
/// and has no workflow identity to borrow.
#[error("assistant session {session_id} was not found")]
AssistantSessionNotFound {
/// The session the append or mutation targeted.
session_id: String,
},
/// The targeted shard is owned by a different node: a quorum write was fenced
/// because this node is not the current owner of the workflow's shard. Unlike
/// [`Self::Backend`] this is a typed, *retryable* routing signal — the caller
/// (or the request-routing edge) should re-resolve the shard's owner and retry
/// or forward, rather than treating it as an opaque internal failure. Emitted
/// only on the distributed (`[store.cluster]`) replication path.
#[error("shard {shard} is owned by another node (not owner)")]
NotOwner {
/// The distribution shard the fenced workflow's durable state lives on.
shard: usize,
},
/// A list request the store refuses on its face: a zero page limit, or a
/// cursor that was not minted under the request's own namespace, filter,
/// and sort. Never a backend fault; the caller's input is wrong.
#[error("invalid list query: {0}")]
InvalidQuery(String),
/// Backend-specific failure mapped into the store contract's closed error surface.
#[error("store backend error: {0}")]
Backend(String),
/// Serialization or deserialization failure while crossing the store boundary.
#[error("store serialization error: {0}")]
Serialization(String),
}