abhigyanpatwari/GitNexus · error · SandboxError

dependency symlink must be a bounded relative link: {relativ

Error message

dependency symlink must be a bounded relative link: {relative}

What it means

Raised by _copy_symlink when the readlink target is empty, is an absolute path (starts with '/'), or contains a NUL byte. The snapshot only stores bounded relative links because absolute links would escape the sandbox workspace at mount time and the snapshot must be reproducible independent of the host's absolute layout. This is a security-containment guard as much as a consistency one.

Source

Thrown at eval/workflow_bench/task_assets.py:474

                sha256=digest.hexdigest(),
                mode=captured_mode,
            )
        )

    def _copy_symlink(
        self,
        parent_descriptor: int,
        name: str,
        relative: PurePosixPath,
        before: os.stat_result,
    ) -> None:
        try:
            target = os.readlink(name, dir_fd=parent_descriptor)
            target_bytes = target.encode("utf-8")
        except (OSError, UnicodeEncodeError) as exc:
            raise SandboxError(f"dependency symlink is unreadable or not UTF-8: {relative}") from exc
        if not target or PurePosixPath(target).is_absolute() or "\x00" in target:
            raise SandboxError(f"dependency symlink must be a bounded relative link: {relative}")
        if len(target_bytes) > MAX_TASK_ASSET_PATH_BYTES:
            raise SandboxError(f"dependency symlink target exceeds the path limit: {relative}")
        if self.budget.total_bytes + len(target_bytes) > MAX_TASK_ASSET_BYTES:
            raise SandboxError("sandbox_copy exceeds the total byte limit")
        destination = self.destination / Path(*relative.parts)
        os.symlink(target, destination)
        after = os.stat(name, dir_fd=parent_descriptor, follow_symlinks=False)
        if (
            _mutation_identity(before) != _mutation_identity(after)
            or os.readlink(
                name,
                dir_fd=parent_descriptor,
            )
            != target
        ):
            raise SandboxError(f"dependency symlink changed while snapshotting: {relative}")
        self.total_bytes += len(target_bytes)
        self.budget.total_bytes += len(target_bytes)

View on GitHub (pinned to d540b00184)

Solutions

  1. Recreate offending symlinks as relative links: `ln -sfn ../<package> node_modules/<link>` using a path relative to the link's own directory.
  2. If using npm/pnpm/yarn, reinstall dependencies inside the repo so the package manager writes relative links: `rm -rf node_modules && npm install`.
  3. Audit with: `find <dep-source> -type l -exec sh -c 'readlink "$1" | grep -q ^/ && echo "$1"' _ {} \;` to list absolute-target links.
  4. For NUL-byte or empty targets, delete and recreate the link cleanly.

Example fix

# before
node_modules/.foo -> /usr/lib/node_modules/foo

# after
rm node_modules/.foo
ln -s ../foo node_modules/.foo   # relative, bounded
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path, PurePosixPath
import os

def validate_symlinks_bounded_relative(dep_source: Path) -> None:
    for current, dirs, files in os.walk(dep_source, followlinks=False):
        for name in dirs + files:
            p = Path(current) / name
            if p.is_symlink():
                target = os.readlink(p)
                if not target or PurePosixPath(target).is_absolute() or "\x00" in target:
                    raise ValueError(f"symlink must be bounded relative: {p} -> {target!r}")

for d in task.get("sandbox_dependencies", []):
    validate_symlinks_bounded_relative(repo_path / d["source"])

Type guard

from pathlib import PurePosixPath
import os
from pathlib import Path

def symlink_target_is_bounded_relative(p: Path) -> bool:
    try:
        t = os.readlink(p)
    except OSError:
        return False
    return bool(t) and not PurePosixPath(t).is_absolute() and "\x00" not in t

Try / catch

from eval.workflow_bench.propposer_sandbox import SandboxError

try:
    snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)
except SandboxError as exc:
    if "bounded relative link" in str(exc):
        # recreate offending links as relative, then re-run
        raise
    raise

Prevention

When it happens

Trigger: A sandbox_dependency source tree contains a symlink pointing to an absolute path (e.g. `/usr/lib/node_modules/foo`), a symlink created with `ln -s '' ...` (empty target), or a malformed link whose target string embeds a NUL byte. Common with system-installed npm packages that symlink into global directories.

Common situations: node_modules created by a system package manager (apt/brew) that links into /usr/lib. A developer hand-created symlink with an absolute target for convenience. Symlinks pointing to /tmp or /home during local dev that got committed or vendored.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/d2a3a41a1be02253. Report an issue: GitHub.