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}