1use std::time::Duration;
4use thiserror::Error;
5
6#[derive(Debug, Clone, Error)]
20pub enum SandboxError {
21 #[error("execution timed out after {timeout:?}")]
23 Timeout {
24 timeout: Duration,
26 },
27
28 #[error("memory limit exceeded: {limit_mb} MB")]
30 MemoryExceeded {
31 limit_mb: u32,
33 },
34
35 #[error("execution failed: {0}")]
37 ExecutionFailed(String),
38
39 #[error("invalid request: {0}")]
41 InvalidRequest(String),
42
43 #[error("backend unavailable: {0}")]
45 BackendUnavailable(String),
46
47 #[error("enforcer '{enforcer}' failed: {message}")]
49 EnforcerFailed {
50 enforcer: String,
52 message: String,
54 },
55
56 #[error("enforcer '{enforcer}' unavailable: {message}")]
58 EnforcerUnavailable {
59 enforcer: String,
61 message: String,
63 },
64
65 #[error("policy violation: {0}")]
67 PolicyViolation(String),
68
69 #[cfg(feature = "workspace")]
72 #[error("provisioning failed for '{resource}': {reason}. {suggestion}")]
73 ProvisionFailed {
74 resource: String,
76 reason: String,
78 suggestion: String,
80 },
81
82 #[cfg(feature = "workspace")]
84 #[error("session '{handle}' not found. It may have been stopped or expired.")]
85 SessionNotFound {
86 handle: String,
88 },
89
90 #[cfg(feature = "workspace")]
92 #[error("snapshot '{id}' not found. It may have been deleted or expired.")]
93 SnapshotNotFound {
94 id: String,
96 },
97
98 #[cfg(feature = "workspace")]
100 #[error("path traversal rejected: '{path}' escapes workspace root. Use relative paths only.")]
101 PathTraversal {
102 path: String,
104 },
105
106 #[cfg(feature = "workspace")]
108 #[error("Docker unavailable: {reason}. Ensure Docker daemon is running and accessible.")]
109 DockerUnavailable {
110 reason: String,
112 },
113
114 #[cfg(feature = "workspace")]
116 #[error(
117 "session timed out after {timeout:?}. Consider increasing session_timeout in SandboxConfig."
118 )]
119 SessionTimeout {
120 timeout: Duration,
122 },
123}
124
125impl From<std::io::Error> for SandboxError {
126 fn from(err: std::io::Error) -> Self {
127 SandboxError::ExecutionFailed(format!("I/O error: {err}"))
128 }
129}
130
131impl From<SandboxError> for adk_core::AdkError {
132 fn from(err: SandboxError) -> Self {
133 use adk_core::{ErrorCategory, ErrorComponent};
134 let (category, code) = match &err {
135 SandboxError::Timeout { .. } => (ErrorCategory::Timeout, "code.sandbox_timeout"),
136 SandboxError::MemoryExceeded { .. } => (ErrorCategory::Internal, "code.sandbox_memory"),
137 SandboxError::ExecutionFailed(_) => (ErrorCategory::Internal, "code.sandbox_execution"),
138 SandboxError::InvalidRequest(_) => {
139 (ErrorCategory::InvalidInput, "code.sandbox_invalid_request")
140 }
141 SandboxError::BackendUnavailable(_) => {
142 (ErrorCategory::Unavailable, "code.sandbox_unavailable")
143 }
144 SandboxError::EnforcerFailed { .. } => {
145 (ErrorCategory::Internal, "code.sandbox_enforcer_failed")
146 }
147 SandboxError::EnforcerUnavailable { .. } => {
148 (ErrorCategory::Unavailable, "code.sandbox_enforcer_unavailable")
149 }
150 SandboxError::PolicyViolation(_) => {
151 (ErrorCategory::InvalidInput, "code.sandbox_policy_violation")
152 }
153 #[cfg(feature = "workspace")]
154 SandboxError::ProvisionFailed { .. } => {
155 (ErrorCategory::Internal, "code.sandbox_provision_failed")
156 }
157 #[cfg(feature = "workspace")]
158 SandboxError::SessionNotFound { .. } => {
159 (ErrorCategory::NotFound, "code.sandbox_session_not_found")
160 }
161 #[cfg(feature = "workspace")]
162 SandboxError::SnapshotNotFound { .. } => {
163 (ErrorCategory::NotFound, "code.sandbox_snapshot_not_found")
164 }
165 #[cfg(feature = "workspace")]
166 SandboxError::PathTraversal { .. } => {
167 (ErrorCategory::InvalidInput, "code.sandbox_path_traversal")
168 }
169 #[cfg(feature = "workspace")]
170 SandboxError::DockerUnavailable { .. } => {
171 (ErrorCategory::Unavailable, "code.sandbox_docker_unavailable")
172 }
173 #[cfg(feature = "workspace")]
174 SandboxError::SessionTimeout { .. } => {
175 (ErrorCategory::Timeout, "code.sandbox_session_timeout")
176 }
177 };
178 adk_core::AdkError::new(ErrorComponent::Code, category, code, err.to_string())
179 .with_source(err)
180 }
181}