Skip to main content

oxibonsai_runtime/
error.rs

1//! Runtime error types.
2
3use oxibonsai_core::error::BonsaiError;
4use oxibonsai_kernels::error::KernelError;
5use oxibonsai_model::error::ModelError;
6
7/// Runtime error type.
8#[derive(Debug, thiserror::Error)]
9pub enum RuntimeError {
10    #[error("core error: {0}")]
11    Core(#[from] BonsaiError),
12
13    #[error("kernel error: {0}")]
14    Kernel(#[from] KernelError),
15
16    #[error("model error: {0}")]
17    Model(#[from] ModelError),
18
19    #[error("tokenizer error: {0}")]
20    Tokenizer(String),
21
22    #[error("generation stopped: {reason}")]
23    GenerationStopped { reason: String },
24
25    #[error("server error: {0}")]
26    Server(String),
27
28    #[error("GGUF file not found: {path}")]
29    FileNotFound { path: String },
30
31    #[error("IO error: {0}")]
32    Io(#[from] std::io::Error),
33
34    #[error("configuration error: {0}")]
35    Config(String),
36
37    #[error("operation timed out: {operation} after {duration_ms}ms")]
38    Timeout {
39        /// Description of the operation that timed out.
40        operation: String,
41        /// Duration in milliseconds before timeout.
42        duration_ms: u64,
43    },
44
45    #[error("circuit breaker is open, requests are being rejected")]
46    CircuitOpen,
47
48    #[error("capacity exhausted: {resource}")]
49    CapacityExhausted {
50        /// Description of the exhausted resource (e.g. "kv_cache", "memory").
51        resource: String,
52    },
53
54    #[error("batch error: {} sub-errors", .0.len())]
55    BatchError(Vec<RuntimeError>),
56}
57
58impl RuntimeError {
59    /// Return a short, stable error code string for monitoring and alerting.
60    pub fn error_code(&self) -> &str {
61        match self {
62            Self::Core(_) => "CORE_ERROR",
63            Self::Kernel(_) => "KERNEL_ERROR",
64            Self::Model(_) => "MODEL_ERROR",
65            Self::Tokenizer(_) => "TOKENIZER_ERROR",
66            Self::GenerationStopped { .. } => "GENERATION_STOPPED",
67            Self::Server(_) => "SERVER_ERROR",
68            Self::FileNotFound { .. } => "FILE_NOT_FOUND",
69            Self::Io(_) => "IO_ERROR",
70            Self::Config(_) => "CONFIG_ERROR",
71            Self::Timeout { .. } => "TIMEOUT",
72            Self::CircuitOpen => "CIRCUIT_OPEN",
73            Self::CapacityExhausted { .. } => "CAPACITY_EXHAUSTED",
74            Self::BatchError(_) => "BATCH_ERROR",
75        }
76    }
77
78    /// Whether this error is potentially recoverable by retrying.
79    pub fn is_retryable(&self) -> bool {
80        match self {
81            Self::Io(_) => true,
82            Self::Timeout { .. } => true,
83            Self::Server(_) => true,
84            Self::CircuitOpen => true,
85            Self::CapacityExhausted { .. } => true,
86            Self::BatchError(errors) => errors.iter().any(|e| e.is_retryable()),
87            _ => false,
88        }
89    }
90}
91
92/// Result type alias.
93pub type RuntimeResult<T> = Result<T, RuntimeError>;