Skip to main content

tokio_actors/
error.rs

1//! Error types surfaced by the Tokio Actors runtime.
2
3use thiserror::Error;
4
5use crate::types::ActorId;
6
7/// Result type for actor operations.
8pub type ActorResult<T> = Result<T, ActorError>;
9
10/// Errors returned from user-defined actor logic.
11#[derive(Debug, Error, Clone)]
12pub enum ActorError {
13    /// A custom error message from the actor.
14    #[error("actor logic error: {0}")]
15    User(String),
16    /// The actor was cancelled.
17    #[error("actor cancelled")]
18    Cancelled,
19    /// The actor panicked during execution.
20    #[error("actor panicked: {0}")]
21    Panic(String),
22    /// A spawn error occurred.
23    #[error(transparent)]
24    Spawn(#[from] SpawnError),
25}
26
27impl ActorError {
28    /// Creates a new user-defined error.
29    pub fn user(message: impl Into<String>) -> Self {
30        Self::User(message.into())
31    }
32}
33
34impl From<TimerError> for ActorError {
35    fn from(value: TimerError) -> Self {
36        ActorError::User(value.to_string())
37    }
38}
39
40/// Failures encountered while sending a message asynchronously.
41#[derive(Debug, Error, Clone)]
42pub enum SendError {
43    /// The actor's mailbox is closed (actor stopped).
44    #[error("mailbox closed")]
45    Closed,
46}
47
48/// Failures encountered while sending without awaiting capacity.
49#[derive(Debug, Error, Clone)]
50pub enum TrySendError {
51    /// The mailbox is full.
52    #[error("mailbox full")]
53    Full,
54    /// The mailbox is closed (actor stopped).
55    #[error("mailbox closed")]
56    Closed,
57}
58
59/// Errors reported when awaiting a response from an actor.
60///
61/// The three variants encode three distinct facts:
62/// - [`Closed`](AskError::Closed): the request was never enqueued - it was
63///   definitely NOT processed. Safe to retry unconditionally.
64/// - [`ResponseDropped`](AskError::ResponseDropped): the actor stopped after
65///   the request was enqueued but before replying - it MAY have been partially
66///   processed. Retry only if the operation is idempotent.
67/// - [`Actor`](AskError::Actor): the handler produced this error - either it
68///   returned `Err` or it panicked ([`ActorError::Panic`]).
69#[derive(Debug, Error, Clone)]
70pub enum AskError {
71    /// The actor's mailbox was closed before the request could be enqueued.
72    #[error("mailbox closed before the request was sent")]
73    Closed,
74    /// The actor dropped the response channel without sending a reply.
75    #[error("actor stopped before replying")]
76    ResponseDropped,
77    /// The actor's handler returned an error or panicked.
78    #[error("actor returned error: {0}")]
79    Actor(ActorError),
80}
81
82/// Failures encountered when spawning a child actor.
83#[derive(Debug, Error, Clone)]
84pub enum SpawnError {
85    /// No Tokio runtime was found in the current context.
86    #[error("tokio runtime handle not in scope")]
87    MissingRuntime,
88    /// A child with the same ID is already registered.
89    #[error("child actor `{0}` already registered")]
90    DuplicateChild(ActorId),
91    /// An actor with the given name is already registered in the system.
92    #[error("actor name `{name}` already taken in system `{system}`")]
93    NameTaken {
94        /// The actor name that was already taken.
95        name: String,
96        /// The system in which the collision occurred.
97        system: String,
98    },
99    /// An actor system with the given name already exists.
100    #[error("actor system `{0}` already exists")]
101    SystemNameTaken(String),
102}
103
104/// Errors emitted by the timer subsystem.
105#[derive(Debug, Error, Clone)]
106pub enum TimerError {
107    /// The specified timer ID was not found (already fired or cancelled).
108    #[error("timer id not found")]
109    NotFound,
110}
111
112/// Errors emitted by the stream subsystem.
113#[derive(Debug, Error, Clone)]
114pub enum StreamError {
115    /// The specified stream ID was not found (already finished or cancelled).
116    #[error("stream id not found")]
117    NotFound,
118}
119
120impl From<StreamError> for ActorError {
121    fn from(value: StreamError) -> Self {
122        ActorError::User(value.to_string())
123    }
124}
125
126/// Errors emitted by the supervision subsystem.
127#[derive(Debug, Error, Clone)]
128pub enum SupervisionError {
129    /// The specified child was not found.
130    #[error("child `{0}` not found")]
131    ChildNotFound(ActorId),
132    /// The restart budget has been exhausted.
133    #[error("restart budget exhausted")]
134    BudgetExhausted,
135    /// The factory closure failed during a restart attempt.
136    #[error("factory failed: {0}")]
137    FactoryFailed(String),
138    /// The actor is not configured as a supervisor.
139    #[error("actor is not configured as a supervisor")]
140    NotASupervisor,
141    /// The operation requires the child to be stopped, but it is running.
142    #[error("child `{0}` is running")]
143    ChildRunning(ActorId),
144    /// The child has a restart in flight or is a member of a pending group
145    /// restart; manual operations must wait for it to settle.
146    #[error("child `{0}` is restarting")]
147    ChildRestarting(ActorId),
148    /// The child did not terminate within the kill grace even after its task
149    /// was aborted - it is stuck in a non-yielding loop (see the crate docs on
150    /// abort limits). The supervisor stops waiting instead of hanging.
151    #[error("child `{0}` is unresponsive to kill")]
152    ChildUnresponsive(ActorId),
153}
154
155impl From<SupervisionError> for ActorError {
156    fn from(value: SupervisionError) -> Self {
157        ActorError::User(value.to_string())
158    }
159}