1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct OpenClawError {
11 pub code: String,
13 pub message: String,
15 pub details: Option<serde_json::Value>,
17 pub status: Option<u16>,
19 pub retry_after: Option<u32>,
21}
22
23impl OpenClawError {
24 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
26 Self {
27 code: code.into(),
28 message: message.into(),
29 details: None,
30 status: None,
31 retry_after: None,
32 }
33 }
34
35 #[must_use]
37 pub fn from_provider_error(e: openclaw_providers::ProviderError) -> Self {
38 use openclaw_providers::ProviderError;
39
40 match &e {
41 ProviderError::Api { status, message } => Self {
42 code: "PROVIDER_API_ERROR".to_string(),
43 message: message.clone(),
44 details: None,
45 status: Some(*status),
46 retry_after: None,
47 },
48 ProviderError::RateLimited { retry_after_secs } => Self {
49 code: "RATE_LIMITED".to_string(),
50 message: "Rate limited by provider".to_string(),
51 details: None,
52 status: Some(429),
53 retry_after: Some(*retry_after_secs as u32),
54 },
55 ProviderError::Network(err) => Self {
56 code: "NETWORK_ERROR".to_string(),
57 message: err.to_string(),
58 details: None,
59 status: None,
60 retry_after: None,
61 },
62 ProviderError::Config(msg) => Self {
63 code: "CONFIG_ERROR".to_string(),
64 message: msg.clone(),
65 details: None,
66 status: Some(400),
67 retry_after: None,
68 },
69 ProviderError::Serialization(err) => Self {
70 code: "SERIALIZATION_ERROR".to_string(),
71 message: err.to_string(),
72 details: None,
73 status: None,
74 retry_after: None,
75 },
76 }
77 }
78
79 #[must_use]
81 pub fn from_credential_error(e: openclaw_core::secrets::CredentialError) -> Self {
82 use openclaw_core::secrets::CredentialError;
83
84 match &e {
85 CredentialError::NotFound(name) => Self {
86 code: "CREDENTIAL_NOT_FOUND".to_string(),
87 message: format!("Credential not found: {name}"),
88 details: None,
89 status: Some(404),
90 retry_after: None,
91 },
92 CredentialError::Crypto(msg) => Self {
93 code: "CRYPTO_ERROR".to_string(),
94 message: msg.clone(),
95 details: None,
96 status: None,
97 retry_after: None,
98 },
99 _ => Self {
100 code: "CREDENTIAL_ERROR".to_string(),
101 message: e.to_string(),
102 details: None,
103 status: None,
104 retry_after: None,
105 },
106 }
107 }
108
109 pub fn config_error(message: impl Into<String>) -> Self {
111 Self::new("CONFIG_ERROR", message)
112 }
113
114 pub fn event_store_error(message: impl Into<String>) -> Self {
116 Self::new("EVENT_STORE_ERROR", message)
117 }
118
119 pub fn validation_error(message: impl Into<String>) -> Self {
121 Self::new("VALIDATION_ERROR", message)
122 }
123
124 pub fn tool_error(message: impl Into<String>) -> Self {
126 Self::new("TOOL_ERROR", message)
127 }
128
129 pub fn agent_error(message: impl Into<String>) -> Self {
131 Self::new("AGENT_ERROR", message)
132 }
133}
134
135impl From<OpenClawError> for napi::Error {
136 fn from(e: OpenClawError) -> Self {
137 let json = serde_json::to_string(&e).unwrap_or_else(|_| e.message.clone());
139 Self::from_reason(json)
140 }
141}
142
143pub fn to_napi_error(code: &str, e: impl std::fmt::Display) -> napi::Error {
145 OpenClawError::new(code, e.to_string()).into()
146}