{"record":{"id":"e5e5c8b62a5711dd","repo":"abhigyanpatwari/GitNexus","slug":"label-must-be-a-real-directory-lexical","errorCode":null,"errorMessage":"{label} must be a real directory: {lexical}","messagePattern":"(.+?) must be a real directory: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/proposer_sandbox.py","lineNumber":551,"sourceCode":"    ]\n\n\ndef require_claude_sandbox_helpers() -> None:\n    \"\"\"Fail before paid work when Claude's mandatory inner sandbox cannot run.\"\"\"\n\n    _resolve_executable(None, \"socat\")\n\n\ndef _real_directory(path: Path, *, label: str) -> Path:\n    \"\"\"Return an absolute directory path without accepting any symlink hop.\"\"\"\n\n    lexical = path.expanduser().absolute()\n    try:\n        mode = lexical.lstat().st_mode\n    except OSError as exc:\n        raise SandboxError(f\"{label} must be a real directory: {lexical}: {exc}\") from exc\n    if stat.S_ISLNK(mode) or not stat.S_ISDIR(mode):\n        raise SandboxError(f\"{label} must be a real directory: {lexical}\")\n    try:\n        resolved = lexical.resolve(strict=True)\n    except OSError as exc:\n        raise SandboxError(f\"{label} must be a real directory: {lexical}: {exc}\") from exc\n    if resolved != lexical:\n        raise SandboxError(f\"{label} must not traverse symlinks: {lexical}\")\n    return lexical\n\n\ndef _safe_repo_source(repo: Path, relative: str, *, label: str) -> tuple[Path, Path]:\n    candidate = PurePosixPath(relative)\n    if candidate.is_absolute() or \"..\" in candidate.parts or not candidate.parts:\n        raise SandboxError(f\"{label} must be a repository-relative path: {relative!r}\")\n    lexical = repo / Path(*candidate.parts)\n    resolved = lexical.resolve()\n    try:\n        resolved.relative_to(repo)\n    except ValueError as exc:","sourceCodeStart":533,"sourceCodeEnd":569,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/proposer_sandbox.py#L533-L569","documentation":"Raised by _real_directory when lstat succeeds but the mode shows a symlink (S_ISLNK) or a non-directory (not S_ISDIR). The function's contract is to return a real directory with no symlink hop, so a symlink at the lexical path or any non-directory inode (regular file, device, socket) is rejected.","triggerScenarios":"The lexical path itself is a symlink (even if its target is a directory) or is not a directory at all — a regular file, a FIFO, a device, or a socket is passed where a directory is required.","commonSituations":"A 'directory' config value is actually a symlink for convenience; path points at a regular file (e.g. /opt/claude/claude binary instead of its parent dir); a mount source was replaced by a symlink to shared storage; a device or socket file occupies the path; operator symlinked /home/agent to /tmp/agent for space and the contract forbids it.","solutions":["Point directly at the real directory, not a symlink: pass the underlying target only after confirming it is a real dir (note _real_directory will still reject any symlink at the lexical position).","Remove the symlink and use bind mounts or a real directory if the contract requires a non-symlink path.","If a file was passed by mistake, pass its parent directory or the correct directory path.","Recreate the path as a real directory (rmtree the symlink, mkdir)."],"exampleFix":"// before\nreal = _real_directory(Path('/opt/claude/shell-prefix'), label='shell prefix')  # it's a symlink\n// after\nsp = Path('/opt/claude/shell-prefix')\nif sp.is_symlink():\n    sp.unlink()\nsp.mkdir(parents=True, mode=0o755)\nreal = _real_directory(sp, label='shell prefix')","handlingStrategy":"validation","validationCode":"import stat\nfrom pathlib import Path\n\ndef is_real_non_symlink_dir(p: Path) -> bool:\n    p = p.expanduser().absolute()\n    try:\n        mode = p.lstat().st_mode\n    except OSError:\n        return False\n    return stat.S_ISDIR(mode) and not stat.S_ISLNK(mode)\n\nassert is_real_non_symlink_dir(candidate)","typeGuard":"import stat\nfrom pathlib import Path\n\ndef is_real_directory_no_symlink(value: object) -> bool:\n    if not isinstance(value, Path):\n        return False\n    p = value.expanduser().absolute()\n    try:\n        mode = p.lstat().st_mode\n    except OSError:\n        return False\n    return stat.S_ISDIR(mode) and not stat.S_ISLNK(mode)","tryCatchPattern":"try:\n    real = _real_directory(path, label=label)\nexcept SandboxError as exc:\n    if 'must be a real directory' in str(exc):\n        if path.is_symlink():\n            raise SystemExit(f'{path} is a symlink; replace with a real directory')\n        raise\n    raise","preventionTips":["Avoid symlinks at directory config positions.","Use bind mounts instead of symlinks where the contract forbids them.","Pass real directories, not convenience symlinks.","Add a setup check that key directories are real dirs."],"tags":["filesystem","symlink","validation","sandbox","security"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}