cloacina_workflow/error.rs
1/*
2 * Copyright 2025-2026 Colliery Software
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! # Error Types
18//!
19//! This module defines the minimal error types needed for workflow authoring.
20//! These errors do not include database or runtime-specific variants, which
21//! are defined in the main `cloacina` crate.
22//!
23//! ## Error Types
24//!
25//! - [`ContextError`]: Errors related to context operations
26//! - [`TaskError`]: Errors that occur during task execution
27//! - [`CheckpointError`]: Errors in task checkpointing
28
29use chrono::{DateTime, Utc};
30use thiserror::Error;
31
32/// Errors that can occur during context operations.
33///
34/// This minimal version only includes errors that can occur without database
35/// or runtime dependencies.
36#[derive(Debug, Error)]
37pub enum ContextError {
38 /// JSON serialization/deserialization error
39 #[error("Serialization error: {0}")]
40 Serialization(#[from] serde_json::Error),
41
42 /// Key not found in context
43 #[error("Key not found: {0}")]
44 KeyNotFound(String),
45
46 /// Type mismatch when retrieving a value
47 #[error("Type mismatch for key {0}")]
48 TypeMismatch(String),
49
50 /// Key already exists when inserting
51 #[error("Key already exists: {0}")]
52 KeyExists(String),
53
54 /// Database operation failed (infrastructure error, not a key issue)
55 #[error("Database error: {0}")]
56 Database(String),
57
58 /// Connection pool exhausted or unavailable
59 #[error("Connection pool error: {0}")]
60 ConnectionPool(String),
61}
62
63/// Errors that can occur during task execution.
64///
65/// Task errors encompass execution failures, context issues, and
66/// any other problems that prevent a task from completing successfully.
67#[derive(Debug, Error)]
68pub enum TaskError {
69 /// Task execution failed with a message
70 #[error("Task execution failed: {message}")]
71 ExecutionFailed {
72 message: String,
73 task_id: String,
74 timestamp: DateTime<Utc>,
75 },
76
77 /// Task dependency not satisfied
78 #[error("Task dependency not satisfied: {dependency} required by {task_id}")]
79 DependencyNotSatisfied { dependency: String, task_id: String },
80
81 /// Task exceeded timeout
82 #[error("Task timeout: {task_id} exceeded {timeout_seconds}s")]
83 Timeout {
84 task_id: String,
85 timeout_seconds: u64,
86 },
87
88 /// Context operation error within a task
89 #[error("Context error in task {task_id}: {error}")]
90 ContextError {
91 task_id: String,
92 error: ContextError,
93 },
94
95 /// Task validation failed
96 #[error("Task validation failed: {message}")]
97 ValidationFailed { message: String },
98
99 /// Unknown error
100 #[error("Unknown error in task {task_id}: {message}")]
101 Unknown { task_id: String, message: String },
102
103 /// Task readiness check failed
104 #[error("Task readiness check failed: {task_id}")]
105 ReadinessCheckFailed { task_id: String },
106
107 /// Trigger rule evaluation failed
108 #[error("Trigger rule evaluation failed: {task_id}")]
109 TriggerRuleFailed { task_id: String },
110}
111
112impl From<ContextError> for TaskError {
113 fn from(error: ContextError) -> Self {
114 TaskError::ContextError {
115 task_id: "unknown".to_string(),
116 error,
117 }
118 }
119}
120
121/// Errors that can occur during task checkpointing.
122///
123/// Checkpoint errors occur when tasks attempt to save intermediate state
124/// for recovery purposes.
125#[derive(Debug, Error)]
126pub enum CheckpointError {
127 /// Failed to save checkpoint
128 #[error("Failed to save checkpoint for task {task_id}: {message}")]
129 SaveFailed { task_id: String, message: String },
130
131 /// Failed to load checkpoint
132 #[error("Failed to load checkpoint for task {task_id}: {message}")]
133 LoadFailed { task_id: String, message: String },
134
135 /// Checkpoint serialization error
136 #[error("Checkpoint serialization error: {0}")]
137 Serialization(#[from] serde_json::Error),
138
139 /// Checkpoint storage error
140 #[error("Checkpoint storage error: {message}")]
141 StorageError { message: String },
142
143 /// Checkpoint validation failed
144 #[error("Checkpoint validation failed: {message}")]
145 ValidationFailed { message: String },
146}