abhigyanpatwari/GitNexus · error · SandboxError
Compound Engineering plugin entries must not be symlinks: {e
Error message
Compound Engineering plugin entries must not be symlinks: {entry.path} What it means
walk() rejects any entry whose lstat mode is a symlink (stat.S_ISLNK). Symlinks anywhere under skills/, scripts/, or assets/ are forbidden because they can escape the bounded source and create TOCTOU windows during the snapshot copy.
Source
Thrown at eval/workflow_bench/runtime_mounts.py:327
if not skills.exists():
raise SandboxError(f"Compound Engineering plugin skills directory is missing: {skills}")
def walk(directory: Path, relative_dir: PurePosixPath) -> Iterator[tuple[PurePosixPath, Path]]:
try:
with os.scandir(directory) as scanned:
entries = sorted(scanned, key=lambda entry: entry.name)
except OSError as exc:
raise SandboxError(f"Compound Engineering plugin directory is unreadable: {directory}: {exc}") from exc
for entry in entries:
relative = relative_dir / entry.name
if _is_forbidden_plugin_path(relative):
continue
try:
metadata = entry.stat(follow_symlinks=False)
except OSError as exc:
raise SandboxError(f"Compound Engineering plugin entry is unreadable: {entry.path}: {exc}") from exc
if stat.S_ISLNK(metadata.st_mode):
raise SandboxError(f"Compound Engineering plugin entries must not be symlinks: {entry.path}")
if stat.S_ISDIR(metadata.st_mode):
yield from walk(Path(entry.path), relative)
elif stat.S_ISREG(metadata.st_mode):
yield relative, Path(entry.path)
else:
raise SandboxError(f"Compound Engineering plugin entries must be regular files: {entry.path}")
for name in _ALLOWED_PLUGIN_DIRS:
directory = source / name
if not directory.exists():
continue
try:
metadata = directory.lstat()
except OSError as exc:
raise SandboxError(f"Compound Engineering plugin component is unreadable: {directory}: {exc}") from exc
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
raise SandboxError(f"Compound Engineering plugin component must be a real directory: {directory}")
yield from walk(directory, PurePosixPath(name))View on GitHub (pinned to d540b00184)
Solutions
- Find symlinks: `find <plugin_dir>/{skills,scripts,assets} -type l`.
- Dereference them in place: `cp -rL --remove-destination <plugin_dir> <plugin_dir_real>` (then validate).
- Re-tar the plugin with `tar -czh` (dereference at archive time) and re-extract.
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def no_symlinks_in_plugin(source: Path) -> bool:
for name in ("skills", "scripts", "assets"):
d = source / name
if not d.exists():
continue
for p in d.rglob("*"):
if p.is_symlink():
return False
return True Try / catch
try:
list(_plugin_files(source))
except SandboxError as exc:
if "must not be symlinks" in str(exc):
# dereference: cp -rL into a clean directory
...
raise Prevention
- Ship plugins as dereferenced tarballs (`tar -czh`).
- Run `find <plugin>/{skills,scripts,assets} -type l` as a pre-flight check.
When it happens
Trigger: Any file (or directory) inside skills/, scripts/, or assets/ is a symbolic link — e.g., scripts/postinstall.sh -> /usr/local/bin/foo, or skills/ce-plan -> ../shared/ce-plan.
Common situations: Plugin shipped from a monorepo that used symlinks for shared code; operator symlinked for convenience; build tool that created symlinks for assets.
Related errors
- pinned GitNexus runtime entrypoint must be regular and non-s
- Compound Engineering plugin manifest directory is unavailabl
- Compound Engineering plugin manifest directory must be real:
- Compound Engineering plugin directory is unreadable: {direct
- Compound Engineering plugin entries must be regular files: {
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/8b3a183b529e2bfb.
Report an issue: GitHub.