use crate::Identity;
use std::time::Duration;
#[derive(Debug)]
pub enum Error {
Send {
identity: Identity,
details: String,
},
Receive {
identity: Identity,
details: String,
},
Timeout {
identity: Identity,
timeout: Duration,
operation: String,
},
Downcast {
identity: Identity,
expected_type: String,
},
Runtime {
identity: Identity,
details: String,
},
MailboxCapacity {
message: String,
},
Join {
identity: Identity,
source: tokio::task::JoinError,
},
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Send {
identity: actor_id,
details,
} => {
write!(
f,
"Failed to send message to actor {}: {}",
actor_id.name(),
details
)
}
Error::Receive {
identity: actor_id,
details,
} => {
write!(
f,
"Failed to receive reply from actor {}: {}",
actor_id.name(),
details
)
}
Error::Timeout {
identity: actor_id,
timeout,
operation,
} => {
write!(
f,
"{} operation to actor {} timed out after {:?}",
operation,
actor_id.name(),
timeout
)
}
Error::Downcast {
identity: actor_id,
expected_type,
} => {
write!(
f,
"Failed to downcast reply from actor {} to expected type '{}'",
actor_id.name(),
expected_type
)
}
Error::Runtime {
identity: actor_id,
details,
} => {
write!(f, "Runtime error in actor {}: {}", actor_id.name(), details)
}
Error::MailboxCapacity { message } => {
write!(f, "Mailbox capacity error: {message}")
}
Error::Join { identity, source } => {
write!(
f,
"Failed to join spawned task from actor {}: {}",
identity.name(),
source
)
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Join { source, .. } => Some(source),
_ => None,
}
}
}
impl Error {
#[must_use]
pub fn is_retryable(&self) -> bool {
matches!(self, Error::Timeout { .. })
}
#[must_use]
pub fn debugging_tips(&self) -> &'static [&'static str] {
match self {
Error::Send { .. } => &[
"Verify the actor is still running with `actor_ref.is_alive()`",
"The actor's mailbox is closed - the actor has terminated",
"Consider using `ActorWeak` for long-lived references",
],
Error::Receive { .. } => &[
"The actor dropped the reply channel before responding",
"Check if the message handler panicked or returned early",
"Verify the handler correctly awaits async operations",
],
Error::Timeout { .. } => &[
"Consider increasing the timeout duration",
"Check if the actor is processing a slow operation",
"Verify there's no deadlock in the message handler",
"Use `tell` instead if you don't need a response",
],
Error::Downcast { .. } => &[
"The handler returned a different type than expected",
"Verify the Message trait impl returns correct Reply type",
"This usually indicates a bug in handler implementation",
],
Error::Runtime { .. } => &[
"Check if on_start() or on_run() returned an error",
"Look for panic messages in the error details field",
"Use `ActorResult::is_start_failed()` or `is_run_failed()` to identify failure phase",
"Call `ActorResult::error()` to get the underlying error details",
"Initialize tracing-subscriber and set RUST_LOG=debug for lifecycle diagnostics",
],
Error::MailboxCapacity { .. } => &[
"Mailbox capacity must be greater than 0",
"set_default_mailbox_capacity() can only be called once",
"Call it early in main() before spawning actors",
],
Error::Join { .. } => &[
"The spawned task panicked or was cancelled by the runtime",
"Run with RUST_BACKTRACE=1 or RUST_BACKTRACE=full for panic details",
"Use `ActorResult::is_join_failed()` to confirm this failure type",
"Check for unwrap(), expect(), or panic!() calls in actor code",
"Verify tokio runtime wasn't shut down while actor was running",
],
}
}
}
pub type Result<T> = std::result::Result<T, Error>;