restate_sdk_shared_core/vm/
errors.rs1use crate::service_protocol::{ContentTypeError, DecodingError, MessageType};
2use crate::{Error, Version};
3use std::borrow::Cow;
4use std::fmt;
5
6#[derive(Copy, Clone, PartialEq, Eq)]
9pub struct InvocationErrorCode(u16);
10
11impl InvocationErrorCode {
12 pub const fn new(code: u16) -> Self {
13 InvocationErrorCode(code)
14 }
15}
16
17impl fmt::Debug for InvocationErrorCode {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 write!(f, "{}", self.0)
20 }
21}
22
23impl fmt::Display for InvocationErrorCode {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 fmt::Debug::fmt(self, f)
26 }
27}
28
29impl From<u16> for InvocationErrorCode {
30 fn from(value: u16) -> Self {
31 InvocationErrorCode(value)
32 }
33}
34
35impl From<u32> for InvocationErrorCode {
36 fn from(value: u32) -> Self {
37 value
38 .try_into()
39 .map(InvocationErrorCode)
40 .unwrap_or(codes::INTERNAL)
41 }
42}
43
44impl From<InvocationErrorCode> for u16 {
45 fn from(value: InvocationErrorCode) -> Self {
46 value.0
47 }
48}
49
50impl From<InvocationErrorCode> for u32 {
51 fn from(value: InvocationErrorCode) -> Self {
52 value.0 as u32
53 }
54}
55
56pub mod codes {
57 use super::InvocationErrorCode;
58
59 pub const BAD_REQUEST: InvocationErrorCode = InvocationErrorCode(400);
60 pub const INTERNAL: InvocationErrorCode = InvocationErrorCode(500);
61 pub const UNSUPPORTED_MEDIA_TYPE: InvocationErrorCode = InvocationErrorCode(415);
62 pub const JOURNAL_MISMATCH: InvocationErrorCode = InvocationErrorCode(570);
63 pub const PROTOCOL_VIOLATION: InvocationErrorCode = InvocationErrorCode(571);
64 pub const AWAITING_TWO_ASYNC_RESULTS: InvocationErrorCode = InvocationErrorCode(572);
65 pub const UNSUPPORTED_FEATURE: InvocationErrorCode = InvocationErrorCode(573);
66}
67
68impl Error {
71 const fn new_const(code: InvocationErrorCode, message: &'static str) -> Self {
72 Error {
73 code: code.0,
74 message: Cow::Borrowed(message),
75 stacktrace: Cow::Borrowed(""),
76 }
77 }
78}
79
80pub const MISSING_CONTENT_TYPE: Error = Error::new_const(
81 codes::UNSUPPORTED_MEDIA_TYPE,
82 "Missing content type when invoking the service deployment",
83);
84
85pub const UNEXPECTED_INPUT_MESSAGE: Error = Error::new_const(
86 codes::PROTOCOL_VIOLATION,
87 "Expected incoming message to be an entry",
88);
89
90pub const KNOWN_ENTRIES_IS_ZERO: Error =
91 Error::new_const(codes::INTERNAL, "Known entries is zero, expected >= 1");
92
93pub const UNEXPECTED_ENTRY_MESSAGE: Error = Error::new_const(
94 codes::PROTOCOL_VIOLATION,
95 "Expected entry messages only when waiting replay entries",
96);
97
98pub const INPUT_CLOSED_WHILE_WAITING_ENTRIES: Error = Error::new_const(
99 codes::PROTOCOL_VIOLATION,
100 "The input was closed while still waiting to receive all journal to replay",
101);
102
103pub const EMPTY_IDEMPOTENCY_KEY: Error = Error::new_const(
104 codes::INTERNAL,
105 "Trying to execute an idempotent request with an empty idempotency key. The idempotency key must be non-empty.",
106);
107
108#[derive(Debug, Clone, thiserror::Error)]
111#[error("The journal replay unexpectedly ended. Expecting to read entry {expected:?} from the replayed journal, but the buffered entries were drained already.")]
112pub struct UnavailableEntryError {
113 expected: MessageType,
114}
115
116impl UnavailableEntryError {
117 pub fn new(expected: MessageType) -> Self {
118 Self { expected }
119 }
120}
121
122#[derive(Debug, thiserror::Error)]
123#[error("Unexpected state '{state:?}' when invoking '{event:?}'")]
124pub struct UnexpectedStateError {
125 state: &'static str,
126 event: Box<dyn fmt::Debug + 'static>,
127}
128
129impl UnexpectedStateError {
130 pub fn new(state: &'static str, event: impl fmt::Debug + 'static) -> Self {
131 Self {
132 state,
133 event: Box::new(event),
134 }
135 }
136}
137
138#[derive(Debug, thiserror::Error)]
139#[error("Replayed journal doesn't match the handler code at index {command_index}.\nThe handler code generated: {expected:#?}\nwhile the replayed entry is: {actual:#?}")]
140pub struct CommandMismatchError<M: fmt::Debug> {
141 command_index: i64,
142 actual: M,
143 expected: M,
144}
145
146impl<M: fmt::Debug> CommandMismatchError<M> {
147 pub fn new(command_index: i64, actual: M, expected: M) -> CommandMismatchError<M> {
148 Self {
149 command_index,
150 actual,
151 expected,
152 }
153 }
154}
155
156impl<M: fmt::Debug> WithInvocationErrorCode for CommandMismatchError<M> {
157 fn code(&self) -> InvocationErrorCode {
158 codes::JOURNAL_MISMATCH
159 }
160}
161
162#[derive(Debug, Clone, thiserror::Error)]
163#[error("Cannot convert a eager state key into UTF-8 String: {0:?}")]
164pub struct BadEagerStateKeyError(#[from] pub(crate) std::string::FromUtf8Error);
165
166#[derive(Debug, Clone, thiserror::Error)]
167#[error("Cannot decode state keys message: {0}")]
168pub struct DecodeStateKeysProst(#[from] pub(crate) prost::DecodeError);
169
170#[derive(Debug, Clone, thiserror::Error)]
171#[error("Cannot decode state keys message: {0}")]
172pub struct DecodeStateKeysUtf8(#[from] pub(crate) std::string::FromUtf8Error);
173
174#[derive(Debug, Clone, thiserror::Error)]
175#[error("Unexpected empty value variant for get eager state")]
176pub struct EmptyGetEagerState;
177
178#[derive(Debug, Clone, thiserror::Error)]
179#[error("Unexpected empty value variant for state keys")]
180pub struct EmptyGetEagerStateKeys;
181
182#[derive(Debug, thiserror::Error)]
183#[error("Feature '{feature}' is not supported by the negotiated protocol version '{current_version}', the minimum required version is '{minimum_required_version}'")]
184pub struct UnsupportedFeatureForNegotiatedVersion {
185 feature: &'static str,
186 current_version: Version,
187 minimum_required_version: Version,
188}
189
190impl UnsupportedFeatureForNegotiatedVersion {
191 pub fn new(
192 feature: &'static str,
193 current_version: Version,
194 minimum_required_version: Version,
195 ) -> Self {
196 Self {
197 feature,
198 current_version,
199 minimum_required_version,
200 }
201 }
202}
203
204#[derive(Debug, Clone, thiserror::Error)]
205#[error("Replayed journal doesn't match the handler code at index {command_index}.\nThe handler code generated a 'get state command',\nwhile the replayed entry is: {actual:#?}")]
206pub struct UnexpectedGetState {
207 pub(crate) command_index: i64,
208 pub(crate) actual: MessageType,
209}
210
211#[derive(Debug, Clone, thiserror::Error)]
212#[error("Replayed journal doesn't match the handler code at index {command_index}.\nThe handler code generated a 'get state keys command',\nwhile the replayed entry is: {actual:#?}")]
213pub struct UnexpectedGetStateKeys {
214 pub(crate) command_index: i64,
215 pub(crate) actual: MessageType,
216}
217
218trait WithInvocationErrorCode {
221 fn code(&self) -> InvocationErrorCode;
222}
223
224impl<T: WithInvocationErrorCode + fmt::Display> From<T> for Error {
225 fn from(value: T) -> Self {
226 Error::new(value.code().0, value.to_string())
227 }
228}
229
230macro_rules! impl_error_code {
231 ($error_type:ident, $code:ident) => {
232 impl WithInvocationErrorCode for $error_type {
233 fn code(&self) -> InvocationErrorCode {
234 codes::$code
235 }
236 }
237 };
238}
239
240impl_error_code!(ContentTypeError, UNSUPPORTED_MEDIA_TYPE);
241impl WithInvocationErrorCode for DecodingError {
242 fn code(&self) -> InvocationErrorCode {
243 match self {
244 DecodingError::UnexpectedMessageType { .. } => codes::JOURNAL_MISMATCH,
245 _ => codes::INTERNAL,
246 }
247 }
248}
249impl_error_code!(UnavailableEntryError, PROTOCOL_VIOLATION);
250impl_error_code!(UnexpectedStateError, PROTOCOL_VIOLATION);
251impl_error_code!(BadEagerStateKeyError, INTERNAL);
252impl_error_code!(DecodeStateKeysProst, PROTOCOL_VIOLATION);
253impl_error_code!(DecodeStateKeysUtf8, PROTOCOL_VIOLATION);
254impl_error_code!(EmptyGetEagerState, PROTOCOL_VIOLATION);
255impl_error_code!(EmptyGetEagerStateKeys, PROTOCOL_VIOLATION);
256impl_error_code!(UnsupportedFeatureForNegotiatedVersion, UNSUPPORTED_FEATURE);
257impl_error_code!(UnexpectedGetState, JOURNAL_MISMATCH);
258impl_error_code!(UnexpectedGetStateKeys, JOURNAL_MISMATCH);