Skip to main content

rivetkit_core/
error.rs

1use rivet_error::*;
2use serde::{Deserialize, Serialize};
3use serde_json::Value as JsonValue;
4
5pub fn public_error_status_code(group: &str, code: &str) -> Option<u16> {
6	match (group, code) {
7		("auth", "forbidden") => Some(403),
8		("actor", "action_not_found") => Some(404),
9		("actor", "action_timed_out") => Some(408),
10		("actor", "aborted") => Some(400),
11		("message", "incoming_too_long" | "outgoing_too_long") => Some(400),
12		(
13			"queue",
14			"full"
15			| "message_too_large"
16			| "message_invalid"
17			| "invalid_payload"
18			| "invalid_completion_payload"
19			| "already_completed"
20			| "previous_message_not_completed"
21			| "complete_not_configured"
22			| "timed_out",
23		) => Some(400),
24		("user", _) => Some(400),
25		_ => None,
26	}
27}
28
29pub(crate) fn is_internal_error(group: &str, code: &str) -> bool {
30	(group == "core" || group == "rivetkit") && code == "internal_error"
31}
32
33pub(crate) fn is_client_error_public(group: &str, code: &str) -> bool {
34	public_error_status_code(group, code).is_some()
35}
36
37/// Masks private error messages before serializing them to clients because they
38/// may contain private implementation details or user data.
39pub(crate) fn client_error_message<'a>(group: &str, code: &str, message: &'a str) -> &'a str {
40	if is_client_error_public(group, code) {
41		message
42	} else {
43		INTERNAL_ERROR.default_message
44	}
45}
46
47/// Drops private error metadata before serializing it to clients because it may
48/// contain private implementation details or user data.
49pub(crate) fn client_error_metadata<'a>(
50	group: &str,
51	code: &str,
52	metadata: Option<&'a JsonValue>,
53) -> Option<&'a JsonValue> {
54	if is_client_error_public(group, code) {
55		metadata
56	} else {
57		None
58	}
59}
60
61#[derive(RivetError, Debug, Clone, Deserialize, Serialize)]
62#[error("actor")]
63pub enum ActorLifecycle {
64	#[error("starting", "Actor is starting.")]
65	Starting,
66
67	#[error("not_ready", "Actor is not ready.")]
68	NotReady,
69
70	#[error("stopping", "Actor is stopping.")]
71	Stopping,
72
73	#[error("destroying", "Actor is destroying.")]
74	Destroying,
75
76	#[error("shutdown_timeout", "Actor shutdown timed out.")]
77	ShutdownTimeout,
78
79	#[error("dropped_reply", "Actor reply channel was dropped without a response.")]
80	DroppedReply,
81
82	#[error(
83		"overloaded",
84		"Actor is overloaded.",
85		"Actor channel '{channel}' is overloaded while attempting to {operation} (capacity {capacity})."
86	)]
87	Overloaded {
88		channel: String,
89		capacity: usize,
90		operation: String,
91	},
92}
93
94#[derive(RivetError, Debug, Clone, Deserialize, Serialize)]
95#[error("actor")]
96pub enum ActorRuntime {
97	#[error(
98		"not_configured",
99		"Actor capability is not configured.",
100		"Actor capability '{component}' is not configured."
101	)]
102	NotConfigured { component: String },
103
104	#[error(
105		"not_found",
106		"Actor resource was not found.",
107		"Actor {resource} '{id}' was not found."
108	)]
109	NotFound { resource: String, id: String },
110
111	#[error(
112		"not_registered",
113		"Actor factory is not registered.",
114		"Actor factory '{actor_name}' is not registered."
115	)]
116	NotRegistered { actor_name: String },
117
118	#[error("missing_input", "Actor input is missing.")]
119	MissingInput,
120
121	#[error(
122		"invalid_operation",
123		"Actor operation is invalid.",
124		"Actor operation '{operation}' is invalid: {reason}"
125	)]
126	InvalidOperation { operation: String, reason: String },
127
128	#[error(
129		"panicked",
130		"Actor task panicked.",
131		"Actor task panicked while running {operation}."
132	)]
133	Panicked { operation: String },
134}
135
136#[derive(RivetError, Debug, Clone, Deserialize, Serialize)]
137#[error("protocol")]
138pub(crate) enum ProtocolError {
139	#[error(
140		"invalid_http_request",
141		"Invalid HTTP request.",
142		"Invalid HTTP request {field}: {reason}"
143	)]
144	InvalidHttpRequest { field: String, reason: String },
145
146	#[error(
147		"invalid_http_response",
148		"Invalid HTTP response.",
149		"Invalid HTTP response {field}: {reason}"
150	)]
151	InvalidHttpResponse { field: String, reason: String },
152
153	#[error(
154		"invalid_actor_connect_request",
155		"Invalid actor-connect request.",
156		"Invalid actor-connect request {field}: {reason}"
157	)]
158	InvalidActorConnectRequest { field: String, reason: String },
159
160	#[error(
161		"invalid_persisted_data",
162		"Invalid persisted actor data.",
163		"Invalid persisted {label}: {reason}"
164	)]
165	InvalidPersistedData { label: String, reason: String },
166
167	#[error(
168		"unsupported_encoding",
169		"Unsupported protocol encoding.",
170		"Unsupported protocol encoding '{encoding}'."
171	)]
172	UnsupportedEncoding { encoding: String },
173}
174
175#[derive(RivetError, Debug, Clone, Deserialize, Serialize)]
176#[error("sqlite")]
177pub(crate) enum SqliteRuntimeError {
178	#[error(
179		"unavailable",
180		"SQLite is unavailable.",
181		"Actor database is not available because rivetkit-core was built without the sqlite feature."
182	)]
183	Unavailable,
184
185	#[error("closed", "SQLite database is closed.")]
186	Closed,
187
188	#[error(
189		"not_configured",
190		"SQLite is not configured.",
191		"SQLite {component} is not configured."
192	)]
193	NotConfigured { component: String },
194
195	#[error(
196		"invalid_bind_parameter",
197		"Invalid SQLite bind parameter.",
198		"Invalid SQLite bind parameter {name}: {reason}"
199	)]
200	InvalidBindParameter { name: String, reason: String },
201
202	#[error(
203		"remote_unavailable",
204		"Remote SQLite is unavailable.",
205		"Remote SQLite is unavailable: {reason}"
206	)]
207	RemoteUnavailable { reason: String },
208
209	#[error(
210		"remote_execution_failed",
211		"Remote SQLite execution failed.",
212		"Remote SQLite execution failed: {message}"
213	)]
214	RemoteExecutionFailed { message: String },
215
216	#[error(
217		"remote_indeterminate_result",
218		"Remote SQLite result is indeterminate.",
219		"Remote SQLite {operation} may have completed, but the envoy disconnected before returning a result."
220	)]
221	RemoteIndeterminateResult { operation: String },
222
223	#[error(
224		"remote_fence_mismatch",
225		"Remote SQLite generation is stale.",
226		"Remote SQLite generation is stale: {reason}"
227	)]
228	RemoteFenceMismatch { reason: String },
229}