wm_core/error.rs
1//! Core error types for `WhiteMagic` v4.
2
3use thiserror::Error;
4
5/// Result type alias for `WhiteMagic` core operations.
6pub type Result<T> = std::result::Result<T, CoreError>;
7
8/// Core error type covering all `WhiteMagic` failure modes.
9#[derive(Debug, Error)]
10pub enum CoreError {
11 /// Tool execution failed
12 #[error("tool error: {0}")]
13 Tool(String),
14
15 /// Memory operation failed
16 #[error("memory error: {0}")]
17 Memory(String),
18
19 /// Governance rule violation
20 #[error("governance violation: {0}")]
21 Governance(String),
22
23 /// Rate limit exceeded
24 #[error("rate limited: {0}")]
25 RateLimited(String),
26
27 /// Circuit breaker open
28 #[error("circuit breaker open for: {0}")]
29 CircuitBreaker(String),
30
31 /// Invalid arguments
32 #[error("invalid args: {0}")]
33 InvalidArgs(String),
34
35 /// Resource not found
36 #[error("not found: {0}")]
37 NotFound(String),
38
39 /// Polyglot bridge error
40 #[error("polyglot error: {0}")]
41 Polyglot(String),
42
43 /// I/O error
44 #[error("io error: {0}")]
45 Io(#[from] std::io::Error),
46
47 /// Serialization error
48 #[error("serialization error: {0}")]
49 Serde(#[from] serde_json::Error),
50
51 /// Internal error (should not happen in normal operation)
52 #[error("internal error: {0}")]
53 Internal(String),
54}
55
56impl CoreError {
57 /// Whether this error is retryable.
58 #[must_use]
59 pub const fn is_retryable(&self) -> bool {
60 matches!(self, Self::RateLimited(_) | Self::CircuitBreaker(_))
61 }
62
63 /// Whether this error indicates a governance violation.
64 #[must_use]
65 pub const fn is_governance(&self) -> bool {
66 matches!(self, Self::Governance(_))
67 }
68
69 /// Whether this error should count against a tool's circuit breaker.
70 ///
71 /// Caller-caused failures (invalid arguments, governance refusals,
72 /// not-found lookups, rate limiting) say nothing about backend health.
73 /// Counting them let a handful of bad requests trip a breaker and block
74 /// VALID requests that followed (2026-09-15 audit: five invalid-galaxy
75 /// creates fast-failed the next correct call).
76 #[must_use]
77 pub const fn counts_as_breaker_failure(&self) -> bool {
78 matches!(
79 self,
80 Self::Tool(_)
81 | Self::Memory(_)
82 | Self::Internal(_)
83 | Self::Io(_)
84 | Self::Polyglot(_)
85 | Self::Serde(_)
86 )
87 }
88}