astrid-runtime/astrid · error

workspace CoW mask path does not exist: {} — refusing to spa

Error message

workspace CoW mask path does not exist: {} — refusing to spawn a child without the intended copy-on-write deny

What it means

wrap_with_process_paths validates every copy-on-write mask path before spawning a sandboxed child. A mask that does not exist means the sandbox wiring is buggy, not that the mask can be skipped: silently dropping it would leave the child un-denied for a security-critical path. The spawn therefore fails closed with NotFound.

Source

Thrown at crates/astrid-workspace/src/sandbox/mod.rs:292

            if !path.exists() {
                return Err(io::Error::new(
                    io::ErrorKind::NotFound,
                    format!("process write path does not exist: {}", path.display()),
                ));
            }
        }

        // Every caller-supplied mask names copy-on-write bookkeeping the child
        // must not reach (the overlayfs upper/work, or the APFS pristine). Each is
        // validated exactly like the worktree and injection paths — absolute,
        // UTF-8, SBPL-safe — because on macOS it is interpolated into the Seatbelt
        // profile; then it must EXIST, since a path that does not exist is a wiring
        // bug, not a no-op (silently skipping it leaves the child un-denied). The
        // deny is security-critical, so either failure fails the spawn closed.
        for masked in extra_masks {
            let _ = validate_sandbox_str(masked, "workspace CoW mask")?;
            if !masked.exists() {
                return Err(io::Error::new(
                    io::ErrorKind::NotFound,
                    format!(
                        "workspace CoW mask path does not exist: {} — refusing to spawn \
                         a child without the intended copy-on-write deny",
                        masked.display()
                    ),
                ));
            }
        }
        for granted in extra_read_paths.iter().chain(extra_write_paths) {
            if extra_masks
                .iter()
                .any(|masked| paths_overlap(granted, masked))
            {
                return Err(io::Error::new(
                    io::ErrorKind::PermissionDenied,
                    format!(
                        "process path {} overlaps a copy-on-write mask",

View on GitHub (pinned to affd8760f4)

Solutions

  1. Verify the mask path exists with ls/stat and correct the spelling or value in the sandbox config
  2. Create the masked path before spawn (e.g. touch/mkdir) if it is meant to be provisioned at startup
  3. Pass only paths produced by the workspace's own mask-resolution helpers instead of hand-built strings
  4. If the mask is genuinely optional, remove it from extra_masks rather than relying on silent skipping

Example fix

// before
masks.push(PathBuf::from("/opt/app/deny-bin"));
// after
let mask = PathBuf::from("/opt/app/deny-bin");
assert!(mask.exists(), "CoW mask must exist before spawn");
masks.push(mask);
Defensive patterns

Strategy: validation

Validate before calling

let masked = Path::new("/opt/app/deny-bin");
if !masked.exists() {
    return Err(io::Error::new(io::ErrorKind::NotFound,
        format!("CoW mask missing: {}", masked.display())));
}

Type guard

fn mask_exists(p: &Path) -> bool { p.exists() }

Try / catch

match wrap_with_process_paths(...) {
    Err(e) if e.kind() == io::ErrorKind::NotFound =>
        eprintln!("fix mask path: {e}"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling wrap_with_process_paths with an entry in extra_masks that points at a nonexistent path (typo, deleted file, path created later, wrong profile root).

Common situations: Config referencing a CoW mask path that was renamed or removed; running before an initialization step that creates the mask target; mounting the sandbox in a fresh container where the mask path was never provisioned.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/23a62992c0b237da. Report an issue: GitHub.