zed-industries/zed · error · std::io::Error

sandbox write grant {} is a symlink, not a directory

Error message

sandbox write grant {} is a symlink, not a directory

What it means

Validation error in CanonicalPathBuf::from_canonical: the configured sandbox write grant path resolves (via an O_NOFOLLOW open) to a symlink rather than a real directory. Symlink grants are rejected because the sandbox must pin a concrete directory inode, not a path that can be redirected.

Source

Thrown at crates/sandbox/src/util/canonical_path.rs:150

        {
            use std::os::unix::fs::OpenOptionsExt as _;
            // `O_NOFOLLOW` makes a symlink *leaf* open the symlink itself
            // (harmless with `O_PATH`) rather than its target, so we can detect
            // and reject it below; intermediate components are still traversed
            // and caught by the canonical-path comparison.
            let file = std::fs::OpenOptions::new()
                .read(true)
                .custom_flags(libc::O_PATH | libc::O_CLOEXEC | libc::O_NOFOLLOW)
                .open(&path)?;
            let fd = OwnedFd::from(file);

            // Reject a symlink leaf outright: a grant must name a real directory,
            // and `readlink` of an `O_PATH|O_NOFOLLOW` fd on a symlink returns
            // the symlink's *own* path (equal to `path`), so the comparison
            // below wouldn't catch it.
            let stat = nix::sys::stat::fstat(fd.as_raw_fd()).map_err(io::Error::from)?;
            if stat.st_mode & libc::S_IFMT == libc::S_IFLNK {
                return Err(io::Error::new(
                    io::ErrorKind::PermissionDenied,
                    format!(
                        "sandbox write grant {} is a symlink, not a directory",
                        path.display()
                    ),
                ));
            }

            // Load-bearing: the pinned inode's real path must still be exactly
            // the approved canonical path. If any component became a symlink
            // after approval, the fd resolves elsewhere and this diverges.
            let current = std::fs::read_link(format!("/proc/self/fd/{}", fd.as_raw_fd()))?;
            if current != path {
                return Err(io::Error::new(
                    io::ErrorKind::PermissionDenied,
                    format!(
                        "sandbox write grant {} was redirected to {}",
                        path.display(),

View on GitHub (pinned to f4178619ac)

Solutions

  1. Grant the real target directory instead of the symlink
  2. Remove the symlink and replace it with an actual directory
  3. If a symlink is intentional, grant its resolved canonical target
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/sandbox/src/util/canonical_path.rs:150 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of zed-industries/zed@f4178619ac (2026-08-20). Data as JSON: /api/errors/c82f99811096e917. Report an issue: GitHub.