1use slim_datapath::api::ProtoName;
5use slim_datapath::errors::{ErrorPayload, MessageContext};
6use thiserror::Error;
8
9use slim_auth::errors::AuthError;
11use slim_datapath::api::{ProtoMessage, ProtoSessionMessageType, ProtoSessionType};
12use slim_datapath::messages::utils::MessageError;
13use slim_mls::errors::MlsError;
14use tonic::Status;
15
16use crate::SessionMessage;
17use crate::subscription_manager::SubscriptionAckError;
18
19#[derive(Error, Debug)]
20pub enum SessionError {
21 #[error("SLIM channel closed")]
23 SlimChannelClosed,
24 #[error("error receiving message from SLIM")]
25 SlimReception(#[from] Status),
26
27 #[error("message error")]
29 MessageError(#[from] MessageError),
30 #[error("invalid group update operation: {0}")]
31 InvalidGroupUpdateOp(i32),
32 #[error("missing removed participant in GroupRemove message")]
33 MissingRemovedParticipantInGroupRemove,
34 #[error("missing new participant in GroupAdd message")]
35 MissingNewParticipantInGroupAdd,
36 #[error("missing group name in JoinRequest message")]
37 MissingGroupNameInJoinRequest,
38 #[error("missing channel name for group session")]
39 MissingChannelName,
40 #[error("session type unknown: {0:?}")]
41 SessionTypeUnknown(ProtoSessionType),
42 #[error("session message type unexpected: {0:?}")]
43 SessionMessageInternalUnexpected(Box<SessionMessage>),
44 #[error("session message type unknown: {0:?}")]
45 SessionMessageTypeUnknown(ProtoSessionMessageType),
46 #[error("message type unexpected: {0:?}")]
47 MessageTypeUnexpected(Box<ProtoMessage>),
48 #[error("session message type unexpected: {0:?}")]
49 SessionMessageTypeUnexpected(ProtoSessionMessageType),
50 #[error("error getting the participants list")]
51 ParticipantsListQueryFailed,
52 #[error("malformed participant settings")]
53 MalformedParticipant,
54 #[error("missing participant settings")]
55 MissingParticipantSettings,
56 #[error("identity key is empty")]
57 SignatureKeyIsEmpty,
58 #[error("identity key collection failed with auth error: {0:?}")]
59 SignatureKeyCollectionFailedWithAuthErr(AuthError),
60 #[error("unexpected error")]
61 UnexpectedError { source: Box<SessionError> },
62
63 #[error("session not found: {0}")]
65 SessionNotFound(u32),
66 #[error("subscription not found: {0}")]
67 SubscriptionNotFound(Box<ProtoName>),
68
69 #[error("session builder: not all required fields set")]
71 SessionBuilderIncomplete,
72 #[error("message lost for session id: {0}")]
73 MessageLost(u32),
74 #[error("session closed")]
75 SessionClosed,
76 #[error("receive timeout waiting for message")]
77 ReceiveTimeout,
78 #[error("session id already used: {0}")]
79 SessionIdAlreadyUsed(u32),
80 #[error("invalid session id: {0}")]
81 InvalidSessionId(u32),
82
83 #[error("mls operation error")]
85 MlsOp(#[from] MlsError),
86
87 #[error("persistence store error")]
89 Persistence(#[from] slim_persistence::PersistenceError),
90 #[error("failed to (de)serialize session persistence record")]
91 PersistenceSerde(#[from] serde_json::Error),
92 #[error("failed to decode persisted field: {0}")]
93 PersistenceDecode(String),
94 #[error("persisted session schema mismatch: expected {expected}, got {got}")]
95 PersistenceSchemaMismatch { expected: u32, got: u32 },
96
97 #[error("auth error: {0}")]
99 Auth(#[from] AuthError),
100 #[error("duplicate control message replay: message_id={message_id}")]
101 ControlMessageReplay { message_id: u32 },
102
103 #[error("error receiving ack for message: {0}")]
105 AckReception(String),
106 #[error("subscription ack failed: {0}")]
107 SubscriptionAckFailed(#[source] SubscriptionAckError),
108 #[error("unknown destination: {0}")]
109 UnknownDestination(Box<ProtoName>),
110
111 #[error("participant not found in group: {0}")]
113 ParticipantNotFound(Box<ProtoName>),
114 #[error("participant already in group: {0}")]
115 ParticipantAlreadyInGroup(Box<ProtoName>),
116 #[error("cannot invite participant to point-to-point session")]
117 CannotInviteToP2P,
118 #[error("cannot remove participant from point-to-point session")]
119 CannotRemoveFromP2P,
120 #[error("cannot close a point-to-point session")]
121 CannotCloseP2P,
122 #[error("cannot rejoin a point-to-point session")]
123 CannotRejoinP2P,
124 #[error("only initiator can modify participants")]
125 NotInitiator,
126
127 #[error("error sending session internal message to session controller")]
129 SessionControllerSendFailed,
130 #[error("error sending new session notification to app")]
131 NewSessionSendFailed,
132 #[error("error sending session delete message to session layer")]
133 SessionDeleteMessageSendFailed,
134 #[error("error sending data message to application")]
135 ApplicationMessageSendFailed,
136 #[error("error sending data message to slim")]
137 SlimMessageSendFailed,
138 #[error("send failure reported from slim: {ctx}")]
139 SlimSendFailure { ctx: Box<ErrorPayload> },
140
141 #[error("session is draining - drop message")]
143 SessionDrainingDrop,
144 #[error("session already closed")]
145 SessionAlreadyClosed,
146 #[error("participant is offline")]
147 ParticipantOffLine,
148 #[error("rejoin failed: epoch mismatch")]
149 RejoinFailed,
150 #[error("moderator not found")]
151 ModeratorNotFound,
152 #[error("another status change is already in progress")]
153 StatusChangeInProgress,
154 #[error("session cleanup failed: {details}")]
155 SessionCleanupFailed { details: String },
156 #[error("message send retries exhausted for id={id}")]
157 MessageSendRetryFailed { id: u32 },
158 #[error("message receive retries exhausted for id={id}")]
159 MessageReceiveRetryFailed { id: u32 },
160 #[error("session sender is shutdown, cannot send messages")]
161 SessionSenderShutdown,
162 #[error("session receiver is shutdown, cannot receive messages")]
163 SessionReceiverShutdown,
164 #[error("missing participant name on timer")]
165 MissingParticipantNameOnTimer,
166
167 #[error("missing payload: {context}")]
169 MissingPayload { context: &'static str },
170 #[error("message build failed: {0}")]
171 MessageBuild(MessageError),
172 #[error("message payload extract failed in {context}: {source}")]
173 PayloadExtract {
174 context: &'static str,
175 source: MessageError,
176 },
177
178 #[error("missing mls payload in welcome message")]
180 WelcomeMessageMissingMlsPayload,
181 #[error("invalid join request payload")]
182 InvalidJoinRequestPayload,
183 #[error("participant disconnected: {0}")]
184 ParticipantDisconnected(ProtoName),
185 #[error("missing participant name on disconnection event")]
186 MissingParticipantNameOnDisconnection,
187
188 #[error("no pending requests for the given key: {0}")]
190 TimerNotFound(u32),
191 #[error("phase not supported for task")]
192 ModeratorTaskUnsupportedPhase,
193 #[error("unexpected timer id: {0}")]
194 ModeratorTaskUnexpectedTimerId(u32),
195 #[error("failed to add participant to session: {source}")]
196 ModeratorTaskAddFailed { source: Box<SessionError> },
197 #[error("failed to remove participant from session: {source}")]
198 ModeratorTaskRemoveFailed { source: Box<SessionError> },
199 #[error("failed to update session: {source}")]
200 ModeratorTaskUpdateFailed { source: Box<SessionError> },
201 #[error("failed to close session: {source}")]
202 ModeratorTaskCloseFailed { source: Box<SessionError> },
203}
204
205impl SessionError {
206 pub fn build_error(err: MessageError) -> Self {
208 SessionError::MessageBuild(err)
209 }
210 pub fn extract_error(context: &'static str, err: MessageError) -> Self {
211 SessionError::PayloadExtract {
212 context,
213 source: err,
214 }
215 }
216 pub fn cleanup_failed<E: std::fmt::Display>(e: E) -> Self {
217 SessionError::SessionCleanupFailed {
218 details: e.to_string(),
219 }
220 }
221
222 pub fn send_retry_failed(id: u32) -> Self {
224 SessionError::MessageSendRetryFailed { id }
225 }
226
227 pub fn receive_retry_failed(id: u32) -> Self {
228 SessionError::MessageReceiveRetryFailed { id }
229 }
230
231 pub fn session_context(&self) -> Option<&MessageContext> {
234 match self {
235 SessionError::SlimSendFailure { ctx } => ctx.session_context.as_ref(),
236 _ => None,
237 }
238 }
239
240 pub fn is_command_message_error(&self) -> bool {
242 self.session_context()
243 .map(|ctx| ctx.get_session_message_type().is_command_message())
244 .unwrap_or(false)
245 }
246}