Skip to main content

openrustclaw_core/
error.rs

1//! Unified error types for OpenRustClaw.
2//!
3//! Uses `thiserror` for library errors. All crates map their errors into these types.
4
5use thiserror::Error;
6
7/// Top-level error type for OpenRustClaw operations.
8#[derive(Debug, Error)]
9pub enum Error {
10    #[error("Provider error: {0}")]
11    Provider(#[from] ProviderError),
12
13    #[error("Database error: {0}")]
14    Database(#[from] DatabaseError),
15
16    #[error("Memory error: {0}")]
17    Memory(#[from] MemoryError),
18
19    #[error("Security error: {0}")]
20    Security(#[from] SecurityError),
21
22    #[error("Scheduler error: {0}")]
23    Scheduler(#[from] SchedulerError),
24
25    #[error("MCP error: {0}")]
26    Mcp(#[from] McpError),
27
28    #[error("Tool error: {0}")]
29    Tool(#[from] ToolError),
30
31    #[error("Gateway error: {0}")]
32    Gateway(#[from] GatewayError),
33
34    #[error("Channel error: {0}")]
35    Channel(#[from] ChannelError),
36
37    #[error("Voice error: {0}")]
38    Voice(#[from] VoiceError),
39
40    #[error("Distributed error: {0}")]
41    Distributed(#[from] DistributedError),
42
43    #[error("Configuration error: {0}")]
44    Config(String),
45
46    #[error("Sidecar error: {0}")]
47    Sidecar(String),
48
49    #[error("Internal error: {0}")]
50    Internal(String),
51}
52
53/// LLM provider errors.
54#[derive(Debug, Error)]
55pub enum ProviderError {
56    #[error("rate limit from {provider} (retry after {retry_after_secs:?}s)")]
57    RateLimited {
58        provider: String,
59        retry_after_secs: Option<u64>,
60    },
61
62    #[error("Authentication failed for {provider}: {message}")]
63    AuthFailed { provider: String, message: String },
64
65    #[error("Model {model} not found on {provider}")]
66    ModelNotFound { provider: String, model: String },
67
68    #[error("Context length exceeded: {used} tokens used, {max} max")]
69    ContextLengthExceeded { used: usize, max: usize },
70
71    #[error("Provider {provider} unavailable: {message}")]
72    Unavailable { provider: String, message: String },
73
74    #[error("Invalid tool call from {provider}: {message}")]
75    InvalidToolCall { provider: String, message: String },
76
77    #[error("Streaming error from {provider}: {message}")]
78    StreamError { provider: String, message: String },
79
80    #[error("Batch error from {provider}: {message}")]
81    BatchError { provider: String, message: String },
82
83    #[error("All providers in fallback chain exhausted")]
84    AllProvidersExhausted,
85
86    #[error("Provider request error: {0}")]
87    Request(String),
88
89    #[error("Provider response parse error: {0}")]
90    Parse(String),
91}
92
93/// Database layer errors.
94#[derive(Debug, Error)]
95pub enum DatabaseError {
96    #[error("Connection error: {0}")]
97    Connection(String),
98
99    #[error("Migration error: {0}")]
100    Migration(String),
101
102    #[error("Query error: {0}")]
103    Query(String),
104
105    #[error("Record not found: {entity} with id {id}")]
106    NotFound { entity: String, id: String },
107
108    #[error("Constraint violation: {0}")]
109    Constraint(String),
110}
111
112/// Memory system errors.
113#[derive(Debug, Error)]
114pub enum MemoryError {
115    #[error("Embedding error: {0}")]
116    Embedding(String),
117
118    #[error("Search error: {0}")]
119    Search(String),
120
121    #[error("Core memory budget exceeded: {used} tokens, max {max}")]
122    CoreMemoryBudgetExceeded { used: usize, max: usize },
123
124    #[error("Deduplication: entry already exists with id {existing_id}")]
125    Duplicate { existing_id: String },
126
127    #[error("Memory store error: {0}")]
128    Store(String),
129}
130
131/// Security errors.
132#[derive(Debug, Error)]
133pub enum SecurityError {
134    #[error("Authentication required")]
135    AuthRequired,
136
137    #[error("Invalid origin: {origin}")]
138    InvalidOrigin { origin: String },
139
140    #[error("Token expired")]
141    TokenExpired,
142
143    #[error("Token invalid: {0}")]
144    TokenInvalid(String),
145
146    #[error("Skill verification failed: {0}")]
147    SkillVerificationFailed(String),
148
149    #[error("Prompt injection detected: {0}")]
150    PromptInjectionDetected(String),
151
152    #[error("Permission denied: {0}")]
153    PermissionDenied(String),
154
155    #[error("Session isolation violation: {0}")]
156    IsolationViolation(String),
157}
158
159/// Scheduler errors.
160#[derive(Debug, Error)]
161pub enum SchedulerError {
162    #[error("Job not found: {0}")]
163    JobNotFound(String),
164
165    #[error("Lease acquisition failed for job {job_id}")]
166    LeaseAcquisitionFailed { job_id: String },
167
168    #[error("Idempotency conflict: job {job_id} already completed with key {key}")]
169    IdempotencyConflict { job_id: String, key: String },
170
171    #[error("Max retries exceeded for job {job_id} (attempts: {attempts})")]
172    MaxRetriesExceeded { job_id: String, attempts: u32 },
173
174    #[error("Workflow execution failed: {0}")]
175    WorkflowFailed(String),
176
177    #[error("Invalid trigger config: {0}")]
178    InvalidTrigger(String),
179
180    #[error("Heartbeat task not found: {0}")]
181    HeartbeatTaskNotFound(String),
182
183    #[error("Invalid cron expression: {0}")]
184    InvalidCronExpression(String),
185
186    #[error("Missing heartbeat condition")]
187    MissingHeartbeatCondition,
188
189    #[error("Missing heartbeat action")]
190    MissingHeartbeatAction,
191
192    #[error("Heartbeat cooldown active for task {0}")]
193    HeartbeatCooldown(String),
194
195    #[error("File watch error: {0}")]
196    FileWatchError(String),
197
198    #[error("Heartbeat action failed: {0}")]
199    HeartbeatActionFailed(String),
200}
201
202/// MCP protocol errors.
203#[derive(Debug, Error)]
204pub enum McpError {
205    #[error("MCP connection error: {0}")]
206    Connection(String),
207
208    #[error("MCP tool not found: {server}/{tool}")]
209    ToolNotFound { server: String, tool: String },
210
211    #[error("MCP tool execution error: {0}")]
212    ToolExecution(String),
213
214    #[error("MCP schema translation error: {0}")]
215    SchemaTranslation(String),
216
217    #[error("MCP transport error: {0}")]
218    Transport(String),
219}
220
221/// Tool execution errors.
222#[derive(Debug, Error)]
223pub enum ToolError {
224    #[error("Tool not found: {0}")]
225    NotFound(String),
226
227    #[error("Tool execution failed: {tool}: {message}")]
228    ExecutionFailed { tool: String, message: String },
229
230    #[error("Tool input validation error: {tool}: {message}")]
231    InputValidation { tool: String, message: String },
232
233    #[error("Tool capability not granted: {tool} requires {capability}")]
234    CapabilityDenied { tool: String, capability: String },
235
236    #[error("Tool sandbox violation: {0}")]
237    SandboxViolation(String),
238
239    #[error("Tool timeout: {tool} exceeded {timeout_ms}ms")]
240    Timeout { tool: String, timeout_ms: u64 },
241}
242
243/// Gateway errors.
244#[derive(Debug, Error)]
245pub enum GatewayError {
246    #[error("WebSocket error: {0}")]
247    WebSocket(String),
248
249    #[error("Session not found: {0}")]
250    SessionNotFound(String),
251
252    #[error("Rate limit exceeded for session {session_id}")]
253    RateLimitExceeded { session_id: String },
254
255    #[error("Connection closed: {0}")]
256    ConnectionClosed(String),
257}
258
259/// Distributed mode errors.
260#[derive(Debug, Error)]
261pub enum DistributedError {
262    #[error("Cluster error: {0}")]
263    Cluster(String),
264
265    #[error("Node error: {0}")]
266    Node(String),
267
268    #[error("Consensus error: {0}")]
269    Consensus(String),
270
271    #[error("Discovery error: {0}")]
272    Discovery(String),
273
274    #[error("Messaging error: {0}")]
275    Messaging(String),
276
277    #[error("Session error: {0}")]
278    Session(String),
279
280    #[error("Memory error: {0}")]
281    Memory(String),
282
283    #[error("Lock error: {lock_name}: {message}")]
284    Lock { lock_name: String, message: String },
285
286    #[error("Task error: {0}")]
287    Task(String),
288
289    #[error("Leader not available")]
290    LeaderNotAvailable,
291
292    #[error("Not leader: current leader is {0:?}")]
293    NotLeader(Option<String>),
294
295    #[error("Node not found: {0}")]
296    NodeNotFound(String),
297
298    #[error("Session not found: {0}")]
299    SessionNotFound(String),
300
301    #[error("Task not found: {0}")]
302    TaskNotFound(String),
303
304    #[error("Split brain detected: conflicting leader {0}")]
305    SplitBrain(String),
306
307    #[error("Quorum not reached: {0} of {1} nodes available")]
308    QuorumNotReached(usize, usize),
309
310    #[error("Timeout: {0}")]
311    Timeout(String),
312
313    #[error("Serialization error: {0}")]
314    Serialization(String),
315
316    #[error("Invalid state transition from {from} to {to}")]
317    InvalidStateTransition { from: String, to: String },
318}
319
320/// Channel integration errors.
321#[derive(Debug, Error)]
322pub enum ChannelError {
323    #[error("{platform} authentication failed: {message}")]
324    AuthFailed { platform: String, message: String },
325
326    #[error("{platform} rate limited (retry after {retry_after_secs:?}s)")]
327    RateLimited {
328        platform: String,
329        retry_after_secs: Option<u64>,
330    },
331
332    #[error("{platform} connection error: {message}")]
333    Connection { platform: String, message: String },
334
335    #[error("{platform} message send failed: {message}")]
336    SendFailed { platform: String, message: String },
337
338    #[error("{platform} invalid message format: {message}")]
339    InvalidFormat { platform: String, message: String },
340
341    #[error("{platform} configuration error: {message}")]
342    Config { platform: String, message: String },
343
344    #[error("{platform} not connected")]
345    NotConnected { platform: String },
346
347    #[error("{platform} Pub/Sub error: {message}")]
348    PubSubError { platform: String, message: String },
349
350    #[error("{platform} permission denied: {message}")]
351    PermissionDenied { platform: String, message: String },
352}
353
354/// Voice system errors.
355#[derive(Debug, Error)]
356pub enum VoiceError {
357    #[error("Wake word error: {0}")]
358    Wake(String),
359
360    #[error("Speech-to-text error: {0}")]
361    Stt(String),
362
363    #[error("Text-to-speech error: {0}")]
364    Tts(String),
365
366    #[error("Audio error: {0}")]
367    Audio(String),
368
369    #[error("Audio device not found: {0}")]
370    DeviceNotFound(String),
371
372    #[error("Model error: {0}")]
373    Model(String),
374
375    #[error("Voice configuration error: {0}")]
376    VoiceConfig(String),
377
378    #[error("Component not initialized: {0}")]
379    NotInitialized(String),
380
381    #[error("Talk mode error: {0}")]
382    TalkMode(String),
383
384    #[error("Voice API error: {0}")]
385    VoiceApi(String),
386
387    #[error("Invalid audio format: {0}")]
388    InvalidFormat(String),
389
390    #[error("Voice timeout: {0}")]
391    VoiceTimeout(String),
392}
393
394/// Convenient Result type alias.
395pub type Result<T> = std::result::Result<T, Error>;