Skip to main content

chronon_core/
error.rs

1//! Error types for Chronon.
2
3use std::error::Error;
4
5use thiserror::Error;
6
7/// Result type alias for Chronon operations.
8pub type Result<T> = std::result::Result<T, ChrononError>;
9
10/// Errors that can occur in Chronon operations.
11///
12/// Returned by [`SchedulerStore`](crate::store::SchedulerStore) implementations, the runtime
13/// builder, and script dispatch. Hosts typically map these to HTTP status codes or log events.
14#[derive(Debug, Error)]
15pub enum ChrononError {
16    /// No script with the requested name is registered or persisted.
17    #[error("script not found: {0}")]
18    ScriptNotFound(String),
19
20    /// No job with the requested id or name exists in storage.
21    #[error("job not found: {0}")]
22    JobNotFound(String),
23
24    /// No run with the requested id exists in storage.
25    #[error("run not found: {0}")]
26    RunNotFound(String),
27
28    /// Cron expression failed validation (syntax or unsupported field).
29    #[error("invalid cron expression: {0}")]
30    InvalidCron(String),
31
32    /// IANA timezone string could not be parsed.
33    #[error("invalid timezone: {0}")]
34    InvalidTimezone(String),
35
36    /// Job parameters, actor JSON, or handler inputs failed validation or deserialization.
37    #[error("parameter error: {0}")]
38    ParamError(String),
39
40    /// Job references a script name that does not match the registered script identity.
41    #[error("script mismatch for job '{job_name}': expected '{expected}', got '{actual}'")]
42    ScriptMismatch {
43        /// Script name recorded on the job revision.
44        expected: String,
45        /// Script name resolved from the live registry or request.
46        actual: String,
47        /// Human-readable job name for error messages.
48        job_name: String,
49    },
50
51    /// Underlying storage backend failed or returned an unexpected condition.
52    #[error("storage error: {message}")]
53    StorageError {
54        /// Human-readable summary (stable for logs and HTTP bodies).
55        message: String,
56        /// Optional underlying backend error for `Error::source` chains.
57        #[source]
58        source: Option<Box<dyn Error + Send + Sync>>,
59    },
60
61    /// Identity / actor reconstruction failed when building script context.
62    #[error("identity error: {0}")]
63    Identity(String),
64
65    /// Catch-all for invariant violations and bugs.
66    #[error("internal error: {0}")]
67    Internal(String),
68
69    /// Internal failure that preserves an underlying [`Error::source`] chain (e.g. transport).
70    #[error("internal error: {message}")]
71    InternalSource {
72        /// Human-readable summary.
73        message: String,
74        /// Underlying cause.
75        #[source]
76        source: Box<dyn Error + Send + Sync>,
77    },
78}
79
80impl ChrononError {
81    /// Storage failure without an underlying source.
82    pub fn storage(message: impl Into<String>) -> Self {
83        Self::StorageError {
84            message: message.into(),
85            source: None,
86        }
87    }
88
89    /// Storage failure wrapping an underlying error.
90    pub fn storage_source(
91        message: impl Into<String>,
92        source: impl Error + Send + Sync + 'static,
93    ) -> Self {
94        Self::StorageError {
95            message: message.into(),
96            source: Some(Box::new(source)),
97        }
98    }
99
100    /// Internal failure wrapping an underlying error (preserves `Error::source`).
101    pub fn internal_source(
102        message: impl Into<String>,
103        source: impl Error + Send + Sync + 'static,
104    ) -> Self {
105        Self::InternalSource {
106            message: message.into(),
107            source: Box::new(source),
108        }
109    }
110}
111
112impl From<serde_json::Error> for ChrononError {
113    fn from(err: serde_json::Error) -> Self {
114        Self::ParamError(err.to_string())
115    }
116}