abhigyanpatwari/GitNexus · error · SandboxError

read-only sandbox path is unavailable: {raw_path}

Error message

read-only sandbox path is unavailable: {raw_path}

What it means

Thrown by `command_prefix_for` when a path in `read_only_paths` cannot be resolved relative to the sandbox clone. The probe does `expanduser().absolute()`, then `relative_to(clone)`, `lstat()`, and `resolve(strict=True)`; any `OSError` or `ValueError` is wrapped as this `SandboxError`.

Source

Thrown at eval/workflow_bench/proposer_sandbox.py:129

    ) -> list[str]:
        """Build a stricter command boundary from this session's fixed roots.

        Model sessions use ``read_only_paths`` to freeze the evaluated skill
        roots. Verifiers use ``read_only_workspace`` so candidate-authored code
        cannot change the credited implementation. Extra mounts are reserved
        for harness-owned, post-session evidence such as hidden oracles.
        """

        additional: list[ReadOnlyMount] = []
        clone = _real_directory(self.clone, label="sandbox clone")
        for raw_path in read_only_paths:
            lexical = raw_path.expanduser().absolute()
            try:
                relative = lexical.relative_to(clone)
                metadata = lexical.lstat()
                resolved = lexical.resolve(strict=True)
            except (OSError, ValueError) as exc:
                raise SandboxError(f"read-only sandbox path is unavailable: {raw_path}") from exc
            if (
                resolved != lexical
                or stat.S_ISLNK(metadata.st_mode)
                or not (stat.S_ISDIR(metadata.st_mode) or stat.S_ISREG(metadata.st_mode))
            ):
                raise SandboxError(f"read-only sandbox path must be real and non-symlink: {raw_path}")
            additional.append(
                ReadOnlyMount(
                    source=lexical,
                    target=f"{SANDBOX_WORKSPACE}/{PurePosixPath(relative.as_posix())}",
                )
            )

        for mount in extra_read_only_mounts:
            source = mount.source.expanduser().absolute()
            try:
                metadata = source.lstat()
                resolved = source.resolve(strict=True)

View on GitHub (pinned to d540b00184)

Solutions

  1. Ensure every `read_only_paths` entry exists and lives under `session.clone` (the harness root).
  2. Resolve the path yourself first: `p = p.expanduser().resolve(strict=True)` and confirm `p.is_relative_to(session.clone)`.
  3. Check permissions on every parent of the path so `lstat` succeeds.
  4. If the path is supposed to live outside the clone, pass it via `extra_read_only_mounts` instead.

Example fix

# before: absolute host path outside the clone
prefix = session.command_prefix_for(read_only_paths=[Path('/etc/oracle.json')])  # -> SandboxError
# after: place the file inside the clone, or use extra_read_only_mounts
session.clone.joinpath('oracle.json').write_text('...')
prefix = session.command_prefix_for(read_only_paths=[session.clone / 'oracle.json'])
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def read_only_path_is_valid(clone: Path, raw_path: Path) -> bool:
    try:
        lexical = raw_path.expanduser().absolute()
        _ = lexical.relative_to(clone)
        lexical.lstat()
        lexical.resolve(strict=True)
    except (OSError, ValueError):
        return False
    return True

# filter before building the prefix
valid = [p for p in read_only_paths if read_only_path_is_valid(session.clone, p)]
prefix = session.command_prefix_for(read_only_paths=valid)

Type guard

from pathlib import Path

def is_real_path_under_clone(clone: Path, p: Path) -> bool:
    """Type guard: p exists, is under clone, and is real."""
    try:
        lexical = p.expanduser().absolute()
        lexical.relative_to(clone)
        meta = lexical.lstat()
        resolved = lexical.resolve(strict=True)
    except (OSError, ValueError):
        return False
    import stat
    return (
        resolved == lexical
        and not stat.S_ISLNK(meta.st_mode)
        and (stat.S_ISDIR(meta.st_mode) or stat.S_ISREG(meta.st_mode))
    )

Try / catch

try:
    prefix = session.command_prefix_for(read_only_paths=paths)
except SandboxError as exc:
    if "read-only sandbox path is unavailable" in str(exc):
        # drop the bad path or move the file inside the clone
        paths = [p for p in paths if read_only_path_is_valid(session.clone, p)]
        prefix = session.command_prefix_for(read_only_paths=paths)
    raise

Prevention

When it happens

Trigger: Calling `session.command_prefix_for(read_only_paths=[...])` with a path that does not exist, is outside the clone (raises `ValueError` from `relative_to`), cannot be `lstat`-ed (permissions), or fails `resolve(strict=True)` (broken symlink, missing link in chain).

Common situations: Passing an absolute host path that is not under the sandbox clone; a typo in the path; the file was deleted between clone and probe; permissions on a parent dir prevent lstat; a path from another worktree.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/7a5e7a033d8f41e8. Report an issue: GitHub.