abhigyanpatwari/GitNexus · error · SandboxError
{label} must be a real directory: {lexical}: {exc}
Error message
{label} must be a real directory: {lexical}: {exc} What it means
Raised by _real_directory when lstat() on the lexical (expanded, absolute) path raises OSError. _real_directory exists to reject any symlink hop and must first stat the path exactly as given; an OSError here (ENOENT, EACCES, ELOOP) means the directory is not even inspectable, so the label-specific message includes the original exception text.
Source
Thrown at eval/workflow_bench/proposer_sandbox.py:549
"--",
*command,
]
def require_claude_sandbox_helpers() -> None:
"""Fail before paid work when Claude's mandatory inner sandbox cannot run."""
_resolve_executable(None, "socat")
def _real_directory(path: Path, *, label: str) -> Path:
"""Return an absolute directory path without accepting any symlink hop."""
lexical = path.expanduser().absolute()
try:
mode = lexical.lstat().st_mode
except OSError as exc:
raise SandboxError(f"{label} must be a real directory: {lexical}: {exc}") from exc
if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode):
raise SandboxError(f"{label} must be a real directory: {lexical}")
try:
resolved = lexical.resolve(strict=True)
except OSError as exc:
raise SandboxError(f"{label} must be a real directory: {lexical}: {exc}") from exc
if resolved != lexical:
raise SandboxError(f"{label} must not traverse symlinks: {lexical}")
return lexical
def _safe_repo_source(repo: Path, relative: str, *, label: str) -> tuple[Path, Path]:
candidate = PurePosixPath(relative)
if candidate.is_absolute() or ".." in candidate.parts or not candidate.parts:
raise SandboxError(f"{label} must be a repository-relative path: {relative!r}")
lexical = repo / Path(*candidate.parts)
resolved = lexical.resolve()
try:View on GitHub (pinned to d540b00184)
Solutions
- Create the directory before validation: path.mkdir(parents=True, exist_ok=True).
- Confirm the path is correct (typo, wrong env var) with ls -ld from the same user.
- Fix permissions on the parent components so lstat succeeds.
- Resolve env vars upstream and fail with a clear config error if unset.
Example fix
// before real = _real_directory(Path(mount_src), label='mount source') # missing dir // after mount_src = Path(mount_src) mount_src.mkdir(parents=True, exist_ok=True) real = _real_directory(mount_src, label='mount source')
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def ensure_real_dir(p: Path) -> Path:
p = p.expanduser().absolute()
try:
p.lstat()
except OSError as exc:
raise ValueError(f'cannot stat {p}: {exc}') from exc
return p
# call ensure_real_dir before _real_directory to get a clearer error Type guard
null
Try / catch
try:
real = _real_directory(path, label=label)
except SandboxError as exc:
if label in str(exc) and 'must be a real directory' in str(exc) and ': ' in str(exc):
path.mkdir(parents=True, exist_ok=True)
real = _real_directory(path, label=label)
raise Prevention
- Create required directories during setup before preflight.
- Validate env-var-derived paths are non-empty and real.
- Fix parent permissions so lstat can traverse.
- Resolve config paths to absolute form early.
When it happens
Trigger: Calling code that funnels a path through _real_directory (mount sources, repo roots, runtime dirs) with a path whose lstat fails: nonexistent path, missing parent, permission denied on a component, too many symlink levels (ELOOP).
Common situations: Config points a directory env var at a path that was never created; a mount source directory is created lazily but the preflight ran before creation; CI runs as a user without read on the parent; a path constructed from an unset env var produced a literal like '/'; a symlink loop in the path prefix.
Related errors
- {label} must be a real directory: {lexical}
- {label} must not traverse symlinks: {lexical}
- {label} must be a real non-symlink directory: {path}
- {label} must be a regular non-symlink file: {path}
- evidence source must be a regular non-symlink file: {path}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/0265458301745664.
Report an issue: GitHub.