adk_managed/types/
error.rs1use thiserror::Error;
4
5#[derive(Debug, Error)]
21#[non_exhaustive]
22pub enum RuntimeError {
23 #[error("invalid request: {message}")]
25 InvalidRequest {
26 message: String,
28 param: Option<String>,
30 },
31
32 #[error("session not found: {session_id}")]
34 NotFound {
35 session_id: String,
37 },
38
39 #[error("conflict: {message}")]
41 Conflict {
42 message: String,
44 },
45
46 #[error("provider error ({provider}): {message}")]
48 ProviderError {
49 provider: String,
51 message: String,
53 },
54
55 #[error("tool timeout: {tool_use_id} after {timeout_secs}s")]
57 ToolTimeout {
58 tool_use_id: String,
60 timeout_secs: u64,
62 },
63
64 #[error("checkpoint failed: {message}")]
66 CheckpointFailed {
67 message: String,
69 },
70
71 #[error("sandbox error: {message}")]
73 SandboxError {
74 message: String,
76 },
77
78 #[error("internal error: {message}")]
80 Internal {
81 message: String,
83 },
84}
85
86impl RuntimeError {
87 pub fn invalid_request(message: impl Into<String>) -> Self {
89 Self::InvalidRequest { message: message.into(), param: None }
90 }
91
92 pub fn invalid_param(message: impl Into<String>, param: impl Into<String>) -> Self {
94 Self::InvalidRequest { message: message.into(), param: Some(param.into()) }
95 }
96
97 pub fn not_found(session_id: impl Into<String>) -> Self {
99 Self::NotFound { session_id: session_id.into() }
100 }
101
102 pub fn conflict(message: impl Into<String>) -> Self {
104 Self::Conflict { message: message.into() }
105 }
106
107 pub fn provider_error(provider: impl Into<String>, message: impl Into<String>) -> Self {
109 Self::ProviderError { provider: provider.into(), message: message.into() }
110 }
111
112 pub fn tool_timeout(tool_use_id: impl Into<String>, timeout_secs: u64) -> Self {
114 Self::ToolTimeout { tool_use_id: tool_use_id.into(), timeout_secs }
115 }
116
117 pub fn checkpoint_failed(message: impl Into<String>) -> Self {
119 Self::CheckpointFailed { message: message.into() }
120 }
121
122 pub fn sandbox_error(message: impl Into<String>) -> Self {
124 Self::SandboxError { message: message.into() }
125 }
126
127 pub fn internal(message: impl Into<String>) -> Self {
129 Self::Internal { message: message.into() }
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_invalid_request_display() {
139 let err = RuntimeError::invalid_request("missing field 'model'");
140 assert_eq!(err.to_string(), "invalid request: missing field 'model'");
141 }
142
143 #[test]
144 fn test_invalid_param_display() {
145 let err = RuntimeError::invalid_param("must be positive", "timeout_ms");
146 assert_eq!(err.to_string(), "invalid request: must be positive");
147 }
148
149 #[test]
150 fn test_not_found_display() {
151 let err = RuntimeError::not_found("ses_abc123");
152 assert_eq!(err.to_string(), "session not found: ses_abc123");
153 }
154
155 #[test]
156 fn test_conflict_display() {
157 let err = RuntimeError::conflict("cannot transition from Archived to Running");
158 assert_eq!(err.to_string(), "conflict: cannot transition from Archived to Running");
159 }
160
161 #[test]
162 fn test_provider_error_display() {
163 let err = RuntimeError::provider_error("openai", "rate limit exceeded");
164 assert_eq!(err.to_string(), "provider error (openai): rate limit exceeded");
165 }
166
167 #[test]
168 fn test_tool_timeout_display() {
169 let err = RuntimeError::tool_timeout("tool_use_xyz", 300);
170 assert_eq!(err.to_string(), "tool timeout: tool_use_xyz after 300s");
171 }
172
173 #[test]
174 fn test_checkpoint_failed_display() {
175 let err = RuntimeError::checkpoint_failed("database connection lost");
176 assert_eq!(err.to_string(), "checkpoint failed: database connection lost");
177 }
178
179 #[test]
180 fn test_sandbox_error_display() {
181 let err = RuntimeError::sandbox_error("container crashed");
182 assert_eq!(err.to_string(), "sandbox error: container crashed");
183 }
184
185 #[test]
186 fn test_internal_error_display() {
187 let err = RuntimeError::internal("unexpected state");
188 assert_eq!(err.to_string(), "internal error: unexpected state");
189 }
190
191 #[test]
192 fn test_error_is_send_sync() {
193 fn assert_send_sync<T: Send + Sync>() {}
194 assert_send_sync::<RuntimeError>();
195 }
196
197 #[test]
198 fn test_error_variants_have_structured_fields() {
199 let err = RuntimeError::ToolTimeout { tool_use_id: "tu_123".to_string(), timeout_secs: 60 };
201
202 if let RuntimeError::ToolTimeout { tool_use_id, timeout_secs } = &err {
203 assert_eq!(tool_use_id, "tu_123");
204 assert_eq!(*timeout_secs, 60);
205 } else {
206 panic!("expected ToolTimeout variant");
207 }
208 }
209}