abhigyanpatwari/GitNexus · error · SandboxError
{label} target has a non-directory or symlink parent: {relat
Error message
{label} target has a non-directory or symlink parent: {relative} What it means
Raised by _prepare_clone_target while walking the intermediate (non-leaf) components of a clone-local target path using os.open with O_NOFOLLOW | O_DIRECTORY. If opening an intermediate component fails, that component is either a symlink (blocked by O_NOFOLLOW) or not a directory (blocked by O_DIRECTORY). The check exists because this runs before bubblewrap: an untrusted tracked parent symlink could otherwise redirect a mount-placeholder write into the host filesystem.
Source
Thrown at eval/workflow_bench/proposer_sandbox.py:602
This runs before Bubblewrap, so ordinary ``Path.mkdir``/``touch`` calls
are not acceptable: an untrusted tracked parent symlink could redirect a
mount placeholder write into the host filesystem.
"""
flags = os.O_RDONLY | os.O_DIRECTORY | getattr(os, "O_CLOEXEC", 0)
nofollow = getattr(os, "O_NOFOLLOW", 0)
current_fd = os.open(clone, flags | nofollow)
try:
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 | nofollowView on GitHub (pinned to d540b00184)
Solutions
- Inspect each intermediate component of the path in the error inside the worktree: `ls -la <worktree>/<each parent part>` and look for a symlink or non-directory entry.
- If a tracked symlink is the cause, remove or restructure it in the repo so the parent chain is real directories.
- If the collision is from a previous run, ensure the worktree is freshly created (make_worktree produces a clean detached worktree) and that no setup step wrote a file across the target's parent path.
- Run with a freshly built clone/worktree to rule out leftover state.
Example fix
// before — intermediate component 'lib' is a committed symlink target: src/lib/node_modules // after — restructure so every parent is a real directory target: src/vendor/node_modules
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path, PurePosixPath
import os, stat
def parents_are_real_dirs(clone: Path, relative: str) -> bool:
fd = os.open(clone, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
try:
for part in PurePosixPath(relative).parts[:-1]:
try:
nxt = os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=fd)
except OSError:
return False
os.close(fd); fd = nxt
finally:
os.close(fd)
return True Try / catch
from .proposer_sandbox import SandboxError
try:
target = _prepare_clone_target(clone, PurePosixPath(rel), directory=want_dir, label='asset')
except SandboxError:
# a parent component is a symlink or non-directory; abort this arm
raise Prevention
- Never commit symlinks along harness-managed target paths.
- Use a freshly created detached worktree per arm.
- Audit task/dependency target paths so every parent component is a real directory.
When it happens
Trigger: _prepare_clone_target(clone, relative, directory=..., label=...) is asked to create/validate a target inside the clone, but one of relative.parts[:-1] (an intermediate directory) is a symlink or a regular file / special node rather than a directory, so `os.open(part, O_RDONLY|O_DIRECTORY|O_NOFOLLOW, dir_fd=current_fd)` raises OSError.
Common situations: A task or dependency-snapshot path traverses a tracked symlink that was committed into the repo (e.g. `lib/foo` is a symlink); a stale file from a previous run collides with a directory component of the new target; the clone was populated by a tool that created intermediate files where directories were expected.
Related errors
- {label} target cannot be a symlink: {relative}
- {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/4024635ab24b26e5.
Report an issue: GitHub.