{"record":{"id":"075e3353f1637f67","repo":"zeroclaw-labs/zeroclaw","slug":"sandbox-exec-not-found-requires-macos","errorCode":null,"errorMessage":"sandbox-exec not found (requires macOS)","messagePattern":"sandbox-exec not found \\(requires macOS\\)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/security/seatbelt.rs","lineNumber":30,"sourceCode":"    policy_dir: PathBuf,\n    /// Path to the generated policy file for this session.\n    policy_path: PathBuf,\n}\n\nimpl SeatbeltSandbox {\n    /// Create a new Seatbelt sandbox, generating a per-session policy file.\n    /// Returns an error if `sandbox-exec` is not available or the policy file\n    /// cannot be written.\n    pub fn new() -> std::io::Result<Self> {\n        Self::with_workspace(None)\n    }\n\n    /// Create a new Seatbelt sandbox for the provided workspace root.\n    /// If no workspace is provided, falls back to the process current\n    /// directory for compatibility with direct construction.\n    pub fn with_workspace(workspace: Option<&Path>) -> std::io::Result<Self> {\n        if !Self::is_installed() {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::NotFound,\n                \"sandbox-exec not found (requires macOS)\",\n            ));\n        }\n\n        let policy_dir = std::env::temp_dir().join(\"zeroclaw-seatbelt\");\n        std::fs::create_dir_all(&policy_dir)?;\n\n        let session_id = uuid::Uuid::new_v4();\n        let policy_path = policy_dir.join(format!(\"{session_id}.sb\"));\n\n        let workspace = workspace\n            .map(Path::to_path_buf)\n            .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(\"/tmp\")));\n        let policy = generate_policy(&workspace);\n        std::fs::write(&policy_path, &policy)?;\n\n        Ok(Self {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/security/seatbelt.rs#L12-L48","documentation":"SeatbeltSandbox::with_workspace first calls is_installed(), which is literally `Path::new(\"/usr/bin/sandbox-exec\").is_file()` (seatbelt.rs:7,60-62); when that fixed path is absent it returns io::ErrorKind::NotFound with this message. sandbox-exec is Apple's Seatbelt policy tool that the backend shells out to, so the error means \"this machine is not a macOS host with sandbox-exec\" — it has nothing to do with the workspace argument. Construction proceeds to write a per-session .sb policy under $TMPDIR/zeroclaw-seatbelt only after this binary check passes.","triggerScenarios":"Constructing SeatbeltSandbox via new(), with_workspace(Some(..)), or probe() on Linux/Windows, in containers, or on any macOS image where /usr/bin/sandbox-exec is missing; auto-detection loops that probe Seatbelt on non-Mac hosts hit it as a routine skip signal.","commonSituations":"Linux CI runners; dev boxes on Linux testing macOS-only code paths; Docker containers (even on macOS hosts, the container filesystem lacks host binaries); hardened or minimal macOS images where the tool was removed (Apple has it deprecated).","solutions":["Confirm the platform and binary: `ls -l /usr/bin/sandbox-exec` on a stock macOS host; on Linux use LandlockSandbox (with the sandbox-landlock feature) instead","Drive selection through probe() and treat ErrorKind::NotFound as \"backend unavailable\" — fall through to the next backend","Gate seatbelt usage with `#[cfg(target_os = \"macos\")]` so non-macOS builds never construct it","If a custom macOS image dropped the binary, restore it or deploy on a stock macOS host"],"exampleFix":"// before\nlet sandbox = SeatbeltSandbox::with_workspace(Some(&workspace))?; // Err(NotFound) off macOS\n\n// after — probe first, fall back to the platform-appropriate backend\nlet sandbox = match SeatbeltSandbox::probe() {\n    Ok(s) => s,\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => {\n        LandlockSandbox::with_workspace(Some(workspace))? // Linux path (needs sandbox-landlock)\n    }\n    Err(e) => return Err(e.into()),\n};","handlingStrategy":"validation","validationCode":"// mirrors SeatbeltSandbox::is_installed()\nfn seatbelt_installed() -> bool {\n    std::path::Path::new(\"/usr/bin/sandbox-exec\").is_file()\n}","typeGuard":"fn is_not_found(e: &std::io::Error) -> bool {\n    e.kind() == std::io::ErrorKind::NotFound\n}","tryCatchPattern":"match SeatbeltSandbox::with_workspace(Some(&ws)) {\n    Ok(s) => s,\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => { /* not macOS: next backend */ }\n    Err(e) => return Err(e.into()), // e.g. policy-file write failure\n}","preventionTips":["Probe seatbelt only in macOS-gated code paths (#[cfg(target_os = \"macos\")])","Use probe() during auto-detection and skip on NotFound instead of propagating","Ensure $TMPDIR is writable so the later policy-file write under zeroclaw-seatbelt cannot fail after the binary check"],"tags":["rust","sandbox","macos","seatbelt","sandbox-exec","not-found"],"backgroundTag":"required-executable-not-found","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}