Skip to main content

rustkernel_core/
error.rs

1//! Error types for RustKernels.
2
3use crate::domain::Domain;
4use thiserror::Error;
5
6/// Result type alias using `KernelError`.
7pub type Result<T> = std::result::Result<T, KernelError>;
8
9/// Errors that can occur during kernel operations.
10#[derive(Debug, Error)]
11pub enum KernelError {
12    /// Kernel not found in registry.
13    #[error("Kernel not found: {0}")]
14    KernelNotFound(String),
15
16    /// Kernel already registered.
17    #[error("Kernel already registered: {0}")]
18    KernelAlreadyRegistered(String),
19
20    /// Invalid kernel state transition.
21    #[error("Invalid state transition from {from} to {to}")]
22    InvalidStateTransition {
23        /// Current state.
24        from: String,
25        /// Attempted target state.
26        to: String,
27    },
28
29    /// Kernel is not in active state.
30    #[error("Kernel is not active: {0}")]
31    KernelNotActive(String),
32
33    /// Input validation failed.
34    #[error("Input validation failed: {0}")]
35    ValidationError(String),
36
37    /// Serialization error.
38    #[error("Serialization error: {0}")]
39    SerializationError(String),
40
41    /// Deserialization error.
42    #[error("Deserialization error: {0}")]
43    DeserializationError(String),
44
45    /// Message queue is full.
46    #[error("Message queue full (capacity: {capacity})")]
47    QueueFull {
48        /// Queue capacity.
49        capacity: usize,
50    },
51
52    /// Message queue is empty.
53    #[error("Message queue empty")]
54    QueueEmpty,
55
56    /// Message too large for queue.
57    #[error("Message too large: {size} bytes (max: {max} bytes)")]
58    MessageTooLarge {
59        /// Actual message size.
60        size: usize,
61        /// Maximum allowed size.
62        max: usize,
63    },
64
65    /// Timeout waiting for response.
66    #[error("Timeout waiting for response after {0:?}")]
67    Timeout(std::time::Duration),
68
69    /// GPU kernel launch failed.
70    #[error("Kernel launch failed: {0}")]
71    LaunchFailed(String),
72
73    /// GPU compilation error.
74    #[error("GPU compilation error: {0}")]
75    CompilationError(String),
76
77    /// GPU device error.
78    #[error("GPU device error: {0}")]
79    DeviceError(String),
80
81    /// Backend not available.
82    #[error("Backend not available: {0}")]
83    BackendNotAvailable(String),
84
85    /// License error.
86    #[error("License error: {0}")]
87    LicenseError(#[from] crate::license::LicenseError),
88
89    /// SLO violation.
90    #[error("SLO violation: {0}")]
91    SLOViolation(String),
92
93    /// Domain not supported.
94    #[error("Domain not supported: {0}")]
95    DomainNotSupported(Domain),
96
97    /// Internal error.
98    #[error("Internal error: {0}")]
99    InternalError(String),
100
101    /// IO error.
102    #[error("IO error: {0}")]
103    IoError(#[from] std::io::Error),
104
105    /// Configuration error.
106    #[error("Configuration error: {0}")]
107    ConfigError(String),
108
109    /// Actor error.
110    #[error("Actor error: {0}")]
111    ActorError(String),
112
113    /// RingKernel error (from underlying runtime).
114    #[error("RingKernel error: {0}")]
115    RingKernelError(String),
116
117    /// K2K (Kernel-to-Kernel) communication error.
118    #[error("K2K error: {0}")]
119    K2KError(String),
120
121    // Enterprise errors (0.3.1)
122    /// Unauthorized access.
123    #[error("Unauthorized: {0}")]
124    Unauthorized(String),
125
126    /// Resource exhausted (quota exceeded, rate limited, etc.).
127    #[error("Resource exhausted: {0}")]
128    ResourceExhausted(String),
129
130    /// Service unavailable (circuit open, degraded, etc.).
131    #[error("Service unavailable: {0}")]
132    ServiceUnavailable(String),
133}
134
135impl KernelError {
136    /// Create a validation error.
137    #[must_use]
138    pub fn validation(msg: impl Into<String>) -> Self {
139        KernelError::ValidationError(msg.into())
140    }
141
142    /// Create an internal error.
143    #[must_use]
144    pub fn internal(msg: impl Into<String>) -> Self {
145        KernelError::InternalError(msg.into())
146    }
147
148    /// Create a kernel not found error.
149    #[must_use]
150    pub fn not_found(id: impl Into<String>) -> Self {
151        KernelError::KernelNotFound(id.into())
152    }
153
154    /// Create a launch failed error.
155    #[must_use]
156    pub fn launch_failed(msg: impl Into<String>) -> Self {
157        KernelError::LaunchFailed(msg.into())
158    }
159
160    /// Create a device error.
161    #[must_use]
162    pub fn device(msg: impl Into<String>) -> Self {
163        KernelError::DeviceError(msg.into())
164    }
165
166    /// Create a K2K error.
167    #[must_use]
168    pub fn k2k(msg: impl Into<String>) -> Self {
169        KernelError::K2KError(msg.into())
170    }
171
172    /// Returns true if this is a recoverable error (safe to retry).
173    #[must_use]
174    pub fn is_recoverable(&self) -> bool {
175        matches!(
176            self,
177            KernelError::QueueFull { .. }
178                | KernelError::QueueEmpty
179                | KernelError::Timeout(_)
180                | KernelError::ServiceUnavailable(_)
181                | KernelError::ResourceExhausted(_)
182        )
183    }
184
185    /// Returns true if this is a client error (invalid input, not found, etc.).
186    #[must_use]
187    pub fn is_client_error(&self) -> bool {
188        matches!(
189            self,
190            KernelError::KernelNotFound(_)
191                | KernelError::ValidationError(_)
192                | KernelError::DeserializationError(_)
193                | KernelError::Unauthorized(_)
194                | KernelError::DomainNotSupported(_)
195        )
196    }
197
198    /// Returns true if this is a license-related error.
199    #[must_use]
200    pub fn is_license_error(&self) -> bool {
201        matches!(self, KernelError::LicenseError(_))
202    }
203
204    /// Returns the suggested HTTP status code for this error.
205    ///
206    /// Centralizes HTTP status mapping so that all ecosystem integrations
207    /// (Axum, Tower, gRPC, Actix) use consistent status codes.
208    #[must_use]
209    pub fn http_status_code(&self) -> u16 {
210        match self {
211            KernelError::KernelNotFound(_) => 404,
212            KernelError::KernelAlreadyRegistered(_) => 409,
213            KernelError::ValidationError(_) => 400,
214            KernelError::DeserializationError(_) => 400,
215            KernelError::SerializationError(_) => 500,
216            KernelError::Unauthorized(_) => 401,
217            KernelError::ResourceExhausted(_) => 429,
218            KernelError::ServiceUnavailable(_) => 503,
219            KernelError::Timeout(_) => 504,
220            KernelError::LicenseError(_) => 403,
221            KernelError::DomainNotSupported(_) => 403,
222            KernelError::QueueFull { .. } => 503,
223            KernelError::MessageTooLarge { .. } => 413,
224            _ => 500,
225        }
226    }
227
228    /// Returns a machine-readable error code string.
229    #[must_use]
230    pub fn error_code(&self) -> &'static str {
231        match self {
232            KernelError::KernelNotFound(_) => "KERNEL_NOT_FOUND",
233            KernelError::KernelAlreadyRegistered(_) => "KERNEL_ALREADY_REGISTERED",
234            KernelError::InvalidStateTransition { .. } => "INVALID_STATE_TRANSITION",
235            KernelError::KernelNotActive(_) => "KERNEL_NOT_ACTIVE",
236            KernelError::ValidationError(_) => "VALIDATION_ERROR",
237            KernelError::SerializationError(_) => "SERIALIZATION_ERROR",
238            KernelError::DeserializationError(_) => "DESERIALIZATION_ERROR",
239            KernelError::QueueFull { .. } => "QUEUE_FULL",
240            KernelError::QueueEmpty => "QUEUE_EMPTY",
241            KernelError::MessageTooLarge { .. } => "MESSAGE_TOO_LARGE",
242            KernelError::Timeout(_) => "TIMEOUT",
243            KernelError::LaunchFailed(_) => "LAUNCH_FAILED",
244            KernelError::CompilationError(_) => "COMPILATION_ERROR",
245            KernelError::DeviceError(_) => "DEVICE_ERROR",
246            KernelError::BackendNotAvailable(_) => "BACKEND_NOT_AVAILABLE",
247            KernelError::LicenseError(_) => "LICENSE_ERROR",
248            KernelError::SLOViolation(_) => "SLO_VIOLATION",
249            KernelError::DomainNotSupported(_) => "DOMAIN_NOT_SUPPORTED",
250            KernelError::InternalError(_) => "INTERNAL_ERROR",
251            KernelError::IoError(_) => "IO_ERROR",
252            KernelError::ConfigError(_) => "CONFIG_ERROR",
253            KernelError::ActorError(_) => "ACTOR_ERROR",
254            KernelError::RingKernelError(_) => "RINGKERNEL_ERROR",
255            KernelError::K2KError(_) => "K2K_ERROR",
256            KernelError::Unauthorized(_) => "UNAUTHORIZED",
257            KernelError::ResourceExhausted(_) => "RESOURCE_EXHAUSTED",
258            KernelError::ServiceUnavailable(_) => "SERVICE_UNAVAILABLE",
259        }
260    }
261}
262
263/// Convert from ringkernel-core errors.
264impl From<ringkernel_core::RingKernelError> for KernelError {
265    fn from(err: ringkernel_core::RingKernelError) -> Self {
266        KernelError::RingKernelError(err.to_string())
267    }
268}