1use std::fmt;
6
7#[derive(Debug)]
9pub enum ActionExecutionError {
10 PortConflict {
12 port: u16,
13 protocol: String,
14 underlying_error: String,
15 },
16
17 PrivilegeDenied {
20 requirement: String,
21 message: String,
22 },
23
24 Fatal(anyhow::Error),
26
27 #[cfg(feature = "sqlite")]
29 SqlError {
30 database_id: u32,
31 query: String,
32 error: String,
33 },
34
35 DocumentationRequired {
38 action_type: String, protocol: String,
40 documentation: String,
41 original_action: serde_json::Value,
42 },
43
44 InvalidActionParameters {
46 action_type: String,
47 parameter_name: String,
48 error_message: String,
49 original_action: serde_json::Value,
50 },
51}
52
53impl fmt::Display for ActionExecutionError {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::PortConflict {
57 port,
58 protocol,
59 underlying_error,
60 } => {
61 write!(
62 f,
63 "Port {} conflict for {} server: {}",
64 port, protocol, underlying_error
65 )
66 }
67 Self::PrivilegeDenied {
68 requirement,
69 message,
70 } => {
71 write!(f, "Privilege denied ({}): {}", requirement, message)
72 }
73 Self::Fatal(e) => write!(f, "Fatal error: {}", e),
74 #[cfg(feature = "sqlite")]
75 Self::SqlError {
76 database_id, error, ..
77 } => {
78 write!(f, "SQL error in database {}: {}", database_id, error)
79 }
80 Self::DocumentationRequired {
81 action_type,
82 protocol,
83 ..
84 } => {
85 write!(
86 f,
87 "Documentation required for {} with protocol {}",
88 action_type, protocol
89 )
90 }
91 Self::InvalidActionParameters {
92 action_type,
93 parameter_name,
94 error_message,
95 ..
96 } => {
97 write!(
98 f,
99 "Invalid parameter '{}' for {}: {}",
100 parameter_name, action_type, error_message
101 )
102 }
103 }
104 }
105}
106
107impl std::error::Error for ActionExecutionError {}
108
109impl ActionExecutionError {
110 pub fn is_retryable(&self) -> bool {
112 #[allow(clippy::match_like_matches_macro)]
113 match self {
114 Self::PortConflict { .. } => true,
115 #[cfg(feature = "sqlite")]
116 Self::SqlError { .. } => true,
117 Self::DocumentationRequired { .. } => true,
118 Self::InvalidActionParameters { .. } => true,
119 _ => false,
120 }
121 }
122
123 pub fn build_correction_message(&self) -> String {
125 match self {
126 Self::PortConflict { port, protocol, .. } => {
127 format!(
128 r#"Your previous action failed during execution.
129
130Error: Port {} is already in use
131
132The port you specified for the {} server is already bound by another process or service.
133Please choose a different port number.
134
135Suggestions:
136- Try port {} (original + 1000)
137- Try port {} (original + 10000)
138- Try port {} (common alternative)
139- Or choose any available port number
140
141Please provide a corrected open_server action with a different port."#,
142 port,
143 protocol,
144 port + 1000,
145 port + 10000,
146 if *port < 1024 { port + 8000 } else { port + 1 }
147 )
148 }
149 Self::PrivilegeDenied {
150 requirement,
151 message,
152 } => {
153 format!(
154 r#"Your previous action failed during execution.
155
156Error: Insufficient privileges
157
158Requirement: {}
159Message: {}
160
161This error typically cannot be resolved by changing parameters. Please inform the user that elevated privileges are required."#,
162 requirement, message
163 )
164 }
165 Self::Fatal(e) => {
166 format!(
167 r#"Your previous action failed during execution.
168
169Error: {}
170
171This error cannot be automatically resolved. Please inform the user of the error."#,
172 e
173 )
174 }
175 #[cfg(feature = "sqlite")]
176 Self::SqlError {
177 database_id,
178 query,
179 error,
180 } => {
181 format!(
182 r#"Your previous SQL query failed during execution.
183
184Database ID: {}
185Query: {}
186Error: {}
187
188The SQL query syntax or logic had an error. Please review the error message and provide a corrected execute_sql action.
189
190Common issues:
191- Table or column names don't exist (check schema with list_databases)
192- SQL syntax errors (check SQL statement structure)
193- Type mismatches or constraint violations
194- Missing WHERE clause or incorrect conditions
195
196Please provide a corrected execute_sql action with valid SQL."#,
197 database_id, query, error
198 )
199 }
200 Self::DocumentationRequired {
201 action_type,
202 protocol,
203 documentation,
204 ..
205 } => {
206 format!(
207 r#"Before executing {} for protocol '{}', you must first read the documentation.
208
209Here is the documentation for '{}':
210
211{}
212
213Now that you have read the documentation, please confirm your {} action by providing it again.
214If you need to modify the action based on the documentation, please do so.
215
216IMPORTANT: You must provide the action again - it will not be automatically retried."#,
217 action_type, protocol, protocol, documentation, action_type
218 )
219 }
220 Self::InvalidActionParameters {
221 action_type,
222 parameter_name,
223 error_message,
224 original_action,
225 } => {
226 format!(
227 r#"Your previous action failed validation.
228
229Action: {}
230Parameter: {}
231Error: {}
232
233Original action you provided:
234{}
235
236Please provide a corrected {} action with the proper parameters.
237
238CRITICAL: Make sure all required fields are included with valid values."#,
239 action_type,
240 parameter_name,
241 error_message,
242 serde_json::to_string_pretty(original_action)
243 .unwrap_or_else(|_| format!("{:?}", original_action)),
244 action_type
245 )
246 }
247 }
248 }
249}
250
251impl From<anyhow::Error> for ActionExecutionError {
252 fn from(err: anyhow::Error) -> Self {
253 Self::Fatal(err)
255 }
256}