mail4agent_messenger/error.rs
1//! [`MessengerError`], this crate's single error type.
2
3/// Every error this crate's public API can return. Sans-I/O by
4/// construction (see the crate doc): this type never itself performs I/O —
5/// it only describes a failure the caller (a shell, or another module in
6/// this crate) already observed.
7#[derive(Debug, thiserror::Error)]
8pub enum MessengerError {
9 /// One of the identifier newtypes in [`crate::ids`] rejected a value.
10 #[error("invalid {kind} {value:?}: {reason}")]
11 InvalidId {
12 /// The newtype that rejected the value (`"UserId"`, `"RoomId"`, …).
13 kind: &'static str,
14 /// The rejected input, verbatim.
15 value: String,
16 /// Why it was rejected.
17 reason: String,
18 },
19
20 /// A wire payload (an HTTP response body, a `/sync` response, …) was
21 /// not valid JSON, or did not match the shape this crate expected of
22 /// it.
23 #[error("failed to decode wire JSON: {0}")]
24 WireDecode(#[from] serde_json::Error),
25
26 /// An HTTP response came back with an error status and a Matrix-shaped
27 /// `{"errcode": ..., "error": ...}` body (client-server API's own
28 /// standard error response shape).
29 #[error("HTTP {status} {errcode}: {error}")]
30 Http {
31 /// The HTTP status code.
32 status: u16,
33 /// The Matrix `errcode` (e.g. `"M_FORBIDDEN"`).
34 errcode: String,
35 /// The Matrix `error` human-readable string.
36 error: String,
37 },
38
39 /// A cryptographic operation (Olm/Megolm/SAS via `mail4agent_vodozemac`,
40 /// or the local record seal) failed. Wrapped as a plain message rather
41 /// than the concrete upstream error type: this crate's own public API
42 /// must not leak `mail4agent_vodozemac`'s error enum shape as part of
43 /// its own contract.
44 #[error("crypto: {0}")]
45 Crypto(String),
46
47 /// A [`crate::store::CryptoStore`]/[`crate::store::StateStore`]
48 /// operation failed.
49 #[error("store: {0}")]
50 Store(#[from] crate::store::StoreError),
51
52 /// Raised when a caller (`crate::outgoing_queue`/a later `sync_engine`
53 /// piece) attempts to release an outgoing request while
54 /// [`crate::persist::FlushEpoch::is_released`] reports it is not yet
55 /// safe to do so — a state mutation the request depends on is not yet
56 /// durably flushed and acknowledged. See the `persist` module doc for
57 /// the exact per-request ordering guarantee this enforces.
58 #[error(
59 "outgoing requests are blocked: a state mutation they depend on is not yet flushed and acknowledged"
60 )]
61 FlushPending,
62
63 /// [`crate::MessengerCommand::SendMessage`] named an `edit_of` target
64 /// this account did not itself send (or that this core has never seen
65 /// at all) — rejected outright by [`crate::MessengerCore::dispatch`]
66 /// rather than silently sent as a replacement every other member's own
67 /// `m.replace` aggregation would ignore anyway
68 /// (`crate::room::relations`'s own module doc: "must come from the
69 /// original sender").
70 #[error("cannot edit {event_id}: not sent by this account, or unknown")]
71 EditNotOwned {
72 /// The event the caller tried to edit.
73 event_id: crate::ids::EventId,
74 },
75
76 /// A [`crate::MessengerCommand`] the core refused to apply. The message
77 /// names the command and the reason (an unknown room, a sealed event, ...).
78 #[error("intent refused: {0}")]
79 IntentRefused(String),
80}
81
82impl From<crate::canonical_json::CanonicalJsonError> for MessengerError {
83 /// [`crate::canonical_json`] is its own small, dependency-light module
84 /// (it only needs `mail4agent_vodozemac`'s signature types, not this crate's
85 /// error type) -- this conversion is what lets `crypto::account` and
86 /// later pieces use `?` against it without that module needing to know
87 /// about [`MessengerError`] itself.
88 fn from(err: crate::canonical_json::CanonicalJsonError) -> Self {
89 MessengerError::Crypto(format!("canonical JSON: {err}"))
90 }
91}