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
- Verify the mask path exists with ls/stat and correct the spelling or value in the sandbox config
- Create the masked path before spawn (e.g. touch/mkdir) if it is meant to be provisioned at startup
- Pass only paths produced by the workspace's own mask-resolution helpers instead of hand-built strings
- 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
- Resolve mask paths through the workspace's own helpers, never hand-built strings
- Canonicalize configured paths once at load time and fail fast on missing entries
- Add an integration test that spawns with the production mask set
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
- capsule materialization target is redirected or not a direct
- leftover capsule authority receipt is not a regular file: {}
- legacy capsule authority root is not a regular directory: {}
- legacy capsule authority root contains a non-regular entry:
- legacy capsule authority root is not a regular directory: {}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/23a62992c0b237da.
Report an issue: GitHub.