abhigyanpatwari/GitNexus · critical · RuntimeError
generated artifact is not a regular non-symlink file: {path}
Error message
generated artifact is not a regular non-symlink file: {path} What it means
Thrown by _bounded_regular_bytes (used when reading the captured final patch) at the lstat gate. The harness refuses to follow a symlink or read a non-regular file as a generated artifact, because a symlink could point outside the worktree and a special file could exfiltrate or corrupt data. Only a plain regular file is accepted.
Source
Thrown at eval/workflow_bench/runner_artifacts.py:436
"--no-ext-diff",
"--no-textconv",
"--shortstat",
orig_sha,
"--",
".",
":(exclude)docs/plans",
":(exclude).claude/skills",
],
)
return parse_shortstat(output)
def _bounded_regular_bytes(path: Path, *, limit: int) -> bytes:
"""Read at most ``limit`` bytes without following a generated link."""
metadata = path.lstat()
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISREG(metadata.st_mode):
raise RuntimeError(f"generated artifact is not a regular non-symlink file: {path}")
nofollow = getattr(os, "O_NOFOLLOW", 0)
descriptor = os.open(path, os.O_RDONLY | nofollow)
try:
opened = os.fstat(descriptor)
if not stat.S_ISREG(opened.st_mode):
raise RuntimeError(f"generated artifact changed type while opening: {path}")
chunks: list[bytes] = []
remaining = limit
while remaining > 0:
chunk = os.read(descriptor, min(64 * 1024, remaining))
if not chunk:
break
chunks.append(chunk)
remaining -= len(chunk)
return b"".join(chunks)
finally:
os.close(descriptor)
View on GitHub (pinned to d540b00184)
Solutions
- Have the agent write the patch as a real file (open in 'xb'/'wb' mode), not via ln -s.
- Remove any pre-existing symlink at the patch path before capture.
- Audit agent tooling for shell `ln -s` usage against artifact paths.
- If the agent legitimately needs to reference another file, copy its bytes rather than linking.
Example fix
// before — agent symlinks the patch
os.symlink('/host/secret', worktree/'final.patch')
// after — write real bytes
(worktree/'final.patch').write_bytes(patch_bytes) Defensive patterns
Strategy: validation
Validate before calling
import stat
from pathlib import Path
def is_regular_nonsymlink(path: Path) -> bool:
try:
st = path.lstat()
except OSError:
return False
return stat.S_ISREG(st.st_mode) Type guard
import stat
from pathlib import Path
def is_regular_nonsymlink(path: Path) -> bool:
try:
st = path.lstat()
except OSError:
return False
return stat.S_ISREG(st.st_mode) Try / catch
try:
data = _bounded_regular_bytes(patch, limit=MAX_PATCH_BYTES)
except RuntimeError as e:
if 'not a regular non-symlink file' in str(e):
# the agent produced a symlink/special file; rewrite as real bytes
raise
raise Prevention
- Write artifacts as real files (open 'wb'/'xb'), never via os.symlink.
- Remove any pre-existing symlink at artifact paths before capture.
- Audit agent shell usage for `ln -s` against artifact filenames.
- Assert is_regular_nonsymlink before reading agent-produced files.
When it happens
Trigger: path.lstat() reports S_ISLNK (the patch is a symlink) or not S_ISREG (it's a directory/socket/device/fifo). The agent-created patch is not a real file.
Common situations: The agent symlinked final.patch to /etc/passwd or another clone's patch; the patch path names a directory; a fifo/socket was created by a buggy tool; the artifact path collides with an existing directory.
Related errors
- generated artifact changed type while opening: {path}
- {label} must be a real non-symlink directory: {path}
- {label} must be a regular non-symlink file: {path}
- {label} changed while opening: {path}
- evidence source must be a regular non-symlink file: {path}
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/5bf2da0a3e9e7e6a.
Report an issue: GitHub.