abhigyanpatwari/GitNexus · error · SandboxError
{label} target cannot be a symlink: {relative}
Error message
{label} target cannot be a symlink: {relative} What it means
Raised by _prepare_clone_target when the leaf component of the target path already exists and os.stat(follow_symlinks=False) reports it as a symlink. A symlink leaf could redirect a mount or file write outside the clone, so staging aborts rather than follow it. This is a hard trust-boundary refusal: the leaf must be a real directory or regular file.
Source
Thrown at eval/workflow_bench/proposer_sandbox.py:612
for part in relative.parts[:-1]:
try:
os.mkdir(part, mode=0o700, dir_fd=current_fd)
except FileExistsError:
pass
try:
next_fd = os.open(part, flags | nofollow, dir_fd=current_fd)
except OSError as exc:
raise SandboxError(f"{label} target has a non-directory or symlink parent: {relative}") from exc
os.close(current_fd)
current_fd = next_fd
leaf = relative.parts[-1]
try:
mode = os.stat(leaf, dir_fd=current_fd, follow_symlinks=False).st_mode
except FileNotFoundError:
mode = None
if mode is not None and stat.S_ISLNK(mode):
raise SandboxError(f"{label} target cannot be a symlink: {relative}")
if directory is True:
if mode is None:
os.mkdir(leaf, mode=0o700, dir_fd=current_fd)
elif not stat.S_ISDIR(mode):
raise SandboxError(f"{label} directory target has the wrong type: {relative}")
elif directory is False:
if mode is None:
file_flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | nofollow
file_fd = os.open(leaf, file_flags, 0o600, dir_fd=current_fd)
os.close(file_fd)
elif not stat.S_ISREG(mode):
raise SandboxError(f"{label} file target has the wrong type: {relative}")
elif mode is not None and not (stat.S_ISREG(mode) or stat.S_ISDIR(mode)):
raise SandboxError(f"{label} target has the wrong type: {relative}")
finally:
os.close(current_fd)
return clone / Path(*relative.parts)
View on GitHub (pinned to d540b00184)
Solutions
- Inspect the leaf path in the error: `ls -la <worktree>/<leaf>` and confirm it is a symlink (`readlink`).
- Remove the offending symlink from the repo at that path, or change the task target so its leaf is not a pre-existing symlink.
- If a setup step created it, ensure setup writes real directories/files (not symlinks) at harness-managed targets.
- Re-run with a clean worktree to confirm the symlink is tracked rather than generated.
Example fix
// before — target leaf is a symlink in the repo <worktree>/.vite-temp -> /tmp/vite // after — remove the tracked symlink so the harness can create a real dir git rm .vite-temp # let _prepare_clone_target mkdir it
Defensive patterns
Strategy: validation
Validate before calling
import os, stat
from pathlib import PurePosixPath
def leaf_is_not_symlink(clone: Path, relative: str) -> bool:
# open the parent dir with O_NOFOLLOW, then lstat the leaf
parts = PurePosixPath(relative).parts
pfd = os.open(clone, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
try:
for p in parts[:-1]:
nxt = os.open(p, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=pfd)
os.close(pfd); pfd = nxt
try:
mode = os.stat(parts[-1], dir_fd=pfd, follow_symlinks=False).st_mode
return not stat.S_ISLNK(mode)
except FileNotFoundError:
return True # absent leaf is fine
finally:
os.close(pfd) Try / catch
from .proposer_sandbox import SandboxError
try:
target = _prepare_clone_target(clone, PurePosixPath(rel), directory=None, label='mount')
except SandboxError as exc:
# leaf is a symlink; remove it or reroute the target
raise Prevention
- Do not commit symlinks at paths the harness uses as mount placeholders.
- Ensure setup steps create real files/dirs, never symlinks, at harness-managed leaves.
- Validate target leaves with lstat before staging.
When it happens
Trigger: _prepare_clone_target reaches the leaf (relative.parts[-1]); the leaf exists; `stat.S_ISLNK(mode)` is true. This happens when a task target path's final component is a tracked or pre-existing symlink inside the clone (e.g. a dependency mount point committed as a symlink).
Common situations: A repo ships a symlink at a path the harness wants to use as a mount placeholder or file target (common in monorepos with `node_modules` or `dist` symlinks); a previous setup step replaced a directory/file with a symlink; cross-platform checkouts materialize a symlink where a file was expected.
Related errors
- {label} target has a non-directory or symlink parent: {relat
- {label} does not exist: {relative}
- {label} directory target has the wrong type: {relative}
- {label} file target has the wrong type: {relative}
- {label} target has the wrong type: {relative}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/bb71709742cbe359.
Report an issue: GitHub.