abhigyanpatwari/GitNexus · error · SandboxError
{label} must be a repository-relative path: {relative!r}
Error message
{label} must be a repository-relative path: {relative!r} What it means
Raised by _safe_repo_source when the relative path is not a valid repository-relative path: candidate.is_absolute() is true, '..' appears in candidate.parts, or candidate.parts is empty. This stops absolute paths, parent-traversal, and empty inputs before any filesystem access, preventing escape from the repository root.
Source
Thrown at eval/workflow_bench/proposer_sandbox.py:564
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:
resolved.relative_to(repo)
except ValueError as exc:
raise SandboxError(f"{label} escapes its allowed repository root: {relative}") from exc
if not resolved.exists():
raise SandboxError(f"{label} does not exist: {relative}")
return lexical, resolved
def _prepare_clone_target(
clone: Path,
relative: PurePosixPath,
*,
directory: bool | None,
label: str,
) -> Path:View on GitHub (pinned to d540b00184)
Solutions
- Pass a true relative path with no '..' segments: derive it via path.relative_to(repo).
- Validate before calling: assert not PurePosixPath(rel).is_absolute() and '..' not in PurePosixPath(rel).parts and PurePosixPath(rel).parts.
- Reject empty input upstream with a clear config error.
- If you genuinely need a parent path, change the design — the boundary forbids it by intent.
Example fix
// before src, resolved = _safe_repo_source(repo, str(some_abs_path), label='task asset') // after rel = PurePosixPath(some_abs_path).relative_to(repo) src, resolved = _safe_repo_source(repo, str(rel), label='task asset')
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import PurePosixPath
def is_repo_relative(rel: str) -> bool:
p = PurePosixPath(rel)
return (not p.is_absolute()
and '..' not in p.parts
and len(p.parts) > 0
and p.name not in {'', '.', '..'})
assert is_repo_relative(relative) Type guard
from pathlib import PurePosixPath
def is_safe_repo_relative(value: object) -> bool:
if not isinstance(value, str):
return False
p = PurePosixPath(value)
return (not p.is_absolute()
and '..' not in p.parts
and len(p.parts) > 0
and p.name not in {'', '.', '..'}) Try / catch
try:
src, resolved = _safe_repo_source(repo, relative, label=label)
except SandboxError as exc:
if 'repository-relative path' in str(exc):
rel = PurePosixPath(relative).relative_to(repo) if PurePosixPath(relative).is_absolute() else PurePosixPath(relative)
src, resolved = _safe_repo_source(repo, str(rel), label=label)
raise Prevention
- Derive relative paths via path.relative_to(repo).
- Reject '..' and absolute paths at input parse time.
- Treat empty path strings as config errors.
- Add unit tests for traversal inputs.
When it happens
Trigger: Calling _safe_repo_source with relative='/' (absolute), '/etc/passwd', '../outside', 'a/../../b' (after PurePosixPath normalizes parts), or '' (empty -> no parts).
Common situations: Caller used str(path) of an absolute Path as the relative argument; user-supplied or config-supplied path string contained '..'; an empty string passed from an unset config key; a path was normalized externally with PurePosixPath and the '..' collapsed oddly.
Related errors
- {label} contains an unsafe path component: {relative}
- unsafe results artifact path: {relative_value!r}
- extra read-only mount target must be absolute: {mount.target
- {label} escapes its allowed repository root: {relative}
- sandbox_copy must be a repository-relative path: {raw!r}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/e664f86ad3c44eba.
Report an issue: GitHub.