Skip to main content

adk_code/
error.rs

1//! Error types for code execution.
2
3use thiserror::Error;
4
5use crate::diagnostics::RustDiagnostic;
6
7/// Errors that can occur during code execution.
8///
9/// Each variant provides actionable context about what went wrong and why.
10///
11/// # Example
12///
13/// ```rust
14/// use adk_code::ExecutionError;
15///
16/// let err = ExecutionError::CompileFailed("missing semicolon on line 3".to_string());
17/// assert!(err.to_string().contains("compilation failed"));
18/// ```
19#[derive(Debug, Error, Clone, PartialEq, Eq)]
20pub enum ExecutionError {
21    /// The backend cannot enforce a requested sandbox policy control.
22    #[error("unsupported policy: {0}")]
23    UnsupportedPolicy(String),
24
25    /// The backend does not support the requested language.
26    #[error("unsupported language: {0}")]
27    UnsupportedLanguage(String),
28
29    /// Rust or other compiled language failed to build.
30    #[error("compilation failed: {0}")]
31    CompileFailed(String),
32
33    /// Execution exceeded the configured timeout.
34    #[error("execution timeout after {0}ms")]
35    Timeout(u64),
36
37    /// Runtime execution failed.
38    #[error("execution failed: {0}")]
39    ExecutionFailed(String),
40
41    /// Execution was rejected before running (e.g., policy or scope check).
42    #[error("rejected: {0}")]
43    Rejected(String),
44
45    /// The execution request is malformed or missing required fields.
46    #[error("invalid request: {0}")]
47    InvalidRequest(String),
48
49    /// Internal error (e.g., thread panic, unexpected runtime failure).
50    #[error("internal error: {0}")]
51    InternalError(String),
52}
53
54/// Errors from the language-aware code pipeline ([`RustExecutor`]).
55///
56/// Unlike [`ExecutionError`] (which covers the legacy executor), `CodeError`
57/// carries structured diagnostics for compile failures and distinguishes
58/// missing dependencies from sandbox-level failures.
59///
60/// # Example
61///
62/// ```rust
63/// use adk_code::CodeError;
64///
65/// let err = CodeError::InvalidCode("missing `fn run()` entry point".to_string());
66/// assert!(err.to_string().contains("invalid code"));
67/// ```
68#[derive(Debug, Clone, Error)]
69pub enum CodeError {
70    /// Compilation produced one or more error-level diagnostics.
71    #[error("compile error: {stderr}")]
72    CompileError {
73        /// Structured diagnostics parsed from `--error-format=json`.
74        diagnostics: Vec<RustDiagnostic>,
75        /// Raw stderr output from the compiler.
76        stderr: String,
77    },
78
79    /// A required dependency could not be located on disk.
80    #[error("dependency not found: {name} (searched: {searched:?})")]
81    DependencyNotFound {
82        /// Crate name that was not found (e.g., `"serde_json"`).
83        name: String,
84        /// Paths that were searched before giving up.
85        searched: Vec<String>,
86    },
87
88    /// The underlying sandbox backend returned an error.
89    #[error("sandbox error: {0}")]
90    Sandbox(#[from] adk_sandbox::SandboxError),
91
92    /// The source code is invalid before compilation is attempted.
93    #[error("invalid code: {0}")]
94    InvalidCode(String),
95}
96
97impl From<ExecutionError> for adk_core::AdkError {
98    fn from(err: ExecutionError) -> Self {
99        use adk_core::{ErrorCategory, ErrorComponent};
100        let (category, code) = match &err {
101            ExecutionError::UnsupportedPolicy(_) => {
102                (ErrorCategory::Unsupported, "code.unsupported_policy")
103            }
104            ExecutionError::UnsupportedLanguage(_) => {
105                (ErrorCategory::Unsupported, "code.unsupported_language")
106            }
107            ExecutionError::CompileFailed(_) => {
108                (ErrorCategory::InvalidInput, "code.compile_failed")
109            }
110            ExecutionError::Timeout(_) => (ErrorCategory::Timeout, "code.timeout"),
111            ExecutionError::ExecutionFailed(_) => {
112                (ErrorCategory::Internal, "code.execution_failed")
113            }
114            ExecutionError::Rejected(_) => (ErrorCategory::Forbidden, "code.rejected"),
115            ExecutionError::InvalidRequest(_) => {
116                (ErrorCategory::InvalidInput, "code.invalid_request")
117            }
118            ExecutionError::InternalError(_) => (ErrorCategory::Internal, "code.internal"),
119        };
120        adk_core::AdkError::new(ErrorComponent::Code, category, code, err.to_string())
121            .with_source(err)
122    }
123}
124
125impl From<CodeError> for adk_core::AdkError {
126    fn from(err: CodeError) -> Self {
127        use adk_core::{ErrorCategory, ErrorComponent};
128        let (category, code) = match &err {
129            CodeError::CompileError { .. } => (ErrorCategory::InvalidInput, "code.compile_error"),
130            CodeError::DependencyNotFound { .. } => {
131                (ErrorCategory::NotFound, "code.dependency_not_found")
132            }
133            CodeError::Sandbox(_) => (ErrorCategory::Internal, "code.sandbox"),
134            CodeError::InvalidCode(_) => (ErrorCategory::InvalidInput, "code.invalid_code"),
135        };
136        adk_core::AdkError::new(ErrorComponent::Code, category, code, err.to_string())
137            .with_source(err)
138    }
139}