Skip to main content

forge_sandbox/
error.rs

1//! Error types for the Forge sandbox.
2
3use thiserror::Error;
4
5/// Errors that can occur during sandbox execution.
6#[derive(Debug, Error)]
7#[non_exhaustive]
8pub enum SandboxError {
9    /// Code failed validation checks.
10    #[error("code validation failed: {reason}")]
11    ValidationFailed {
12        /// What went wrong.
13        reason: String,
14    },
15
16    /// Code exceeds the configured maximum size.
17    #[error("code exceeds maximum size of {max} bytes (got {actual})")]
18    CodeTooLarge {
19        /// Maximum allowed size.
20        max: usize,
21        /// Actual size.
22        actual: usize,
23    },
24
25    /// Execution result exceeds the configured maximum size.
26    #[error("output exceeds maximum size of {max} bytes")]
27    OutputTooLarge {
28        /// Maximum allowed size.
29        max: usize,
30    },
31
32    /// Execution timed out (async event loop or CPU-bound watchdog).
33    #[error("execution timed out after {timeout_ms}ms")]
34    Timeout {
35        /// Configured timeout in milliseconds.
36        timeout_ms: u64,
37    },
38
39    /// A banned code pattern was detected during validation.
40    #[error("banned pattern detected: `{pattern}` — the sandbox has no filesystem, network, or module access. Use forge.callTool() or forge.server() to interact with external services.")]
41    BannedPattern {
42        /// The pattern that was matched.
43        pattern: String,
44    },
45
46    /// Generic execution failure.
47    #[error("sandbox execution failed: {0}")]
48    Execution(#[from] anyhow::Error),
49
50    /// A JavaScript error was thrown during execution.
51    #[error("javascript error: {message}")]
52    JsError {
53        /// The error message from JavaScript.
54        message: String,
55    },
56
57    /// Result serialization failed.
58    #[error("result serialization failed: {0}")]
59    Serialization(#[from] serde_json::Error),
60
61    /// Too many concurrent sandbox executions.
62    #[error("concurrency limit reached (max {max} concurrent executions)")]
63    ConcurrencyLimit {
64        /// Maximum allowed concurrent executions.
65        max: usize,
66    },
67
68    /// Too many tool calls in a single execution.
69    #[error("tool call limit exceeded (max {max} calls per execution)")]
70    ToolCallLimit {
71        /// Maximum allowed tool calls.
72        max: usize,
73    },
74
75    /// Tool call arguments exceed the configured maximum size.
76    #[error("tool call arguments too large (max {max} bytes, got {actual})")]
77    ToolCallArgsTooLarge {
78        /// Maximum allowed argument size.
79        max: usize,
80        /// Actual argument size.
81        actual: usize,
82    },
83
84    /// V8 heap memory limit was exceeded.
85    #[error("V8 heap limit exceeded")]
86    HeapLimitExceeded,
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use crate::executor::ExecutionMode;
93    use crate::ipc::ErrorKind;
94
95    /// Compile-time guard: all sandbox public enums are #[non_exhaustive].
96    /// The wildcard arm documents that downstream crates must handle future variants.
97    #[test]
98    #[allow(unreachable_patterns)]
99    fn ne_sandbox_enums_are_non_exhaustive() {
100        let err = SandboxError::HeapLimitExceeded;
101        match err {
102            SandboxError::HeapLimitExceeded
103            | SandboxError::Timeout { .. }
104            | SandboxError::ValidationFailed { .. } => {}
105            _ => {}
106        }
107
108        let mode = ExecutionMode::InProcess;
109        match mode {
110            ExecutionMode::InProcess | ExecutionMode::ChildProcess => {}
111            _ => {}
112        }
113
114        let kind = ErrorKind::Timeout;
115        match kind {
116            ErrorKind::Timeout | ErrorKind::HeapLimit | ErrorKind::JsError => {}
117            _ => {}
118        }
119    }
120}