abhigyanpatwari/GitNexus · error · ValueError
phase artifact must be a regular non-symlink file: {relative
Error message
phase artifact must be a regular non-symlink file: {relative} What it means
Raised by enforce_phase_workspace (runner_artifacts.py:209) when the declared artifact exists in the after snapshot but its state does not start with 'f:' (i.e. it is not a regular file). The phase artifact must be a regular, non-symlink file so its content can be hashed and diffed; a directory, symlink, socket, or device node as the artifact is rejected.
Source
Thrown at eval/workflow_bench/runner_artifacts.py:209
*,
allowed_artifact: Path,
) -> None:
"""Require a phase to change only its one explicit workspace artifact."""
root = worktree.expanduser().absolute()
artifact = allowed_artifact.expanduser().absolute()
try:
relative = PurePosixPath(artifact.relative_to(root).as_posix())
except ValueError as exc:
raise ValueError(f"phase artifact escapes the workspace: {allowed_artifact}") from exc
after = workspace_snapshot(root)
changed = {path for path in before.keys() | after.keys() if before.get(path) != after.get(path)}
artifact_key = relative.as_posix()
artifact_state = after.get(artifact_key)
if before.get(artifact_key) == artifact_state:
raise ValueError(f"phase did not create or change its required artifact: {relative}")
if artifact_state is None or not artifact_state.startswith("f:"):
raise ValueError(f"phase artifact must be a regular non-symlink file: {relative}")
try:
metadata = artifact.lstat()
descriptor = os.open(artifact, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0))
except OSError as exc:
raise ValueError(f"phase artifact must be a readable regular non-symlink file: {relative}") from exc
try:
opened = os.fstat(descriptor)
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not stat.S_ISREG(opened.st_mode)
or metadata.st_dev != opened.st_dev
or metadata.st_ino != opened.st_ino
):
raise ValueError(f"phase artifact must be a regular non-symlink file: {relative}")
finally:
os.close(descriptor)View on GitHub (pinned to d540b00184)
Solutions
- Inspect the after snapshot entry for artifact_key to see its actual type (d/l/s prefix); have the model write a regular file instead of a directory or symlink.
- If the artifact is genuinely missing (artifact_state None), the phase did not produce it — see error 470 remedies.
- Adjust the prompt to require a regular file at the exact path and forbid symlinks/directories.
Example fix
# before: model creates a directory or symlink at the artifact path mkdir docs/plans/plan.md # or: ln -s ../template.md docs/plans/plan.md # after: model writes a regular file cat > docs/plans/plan.md <<'EOF' # Plan ... EOF
Defensive patterns
Strategy: validation
Validate before calling
import os, stat
from pathlib import Path
artifact = Path(allowed_artifact).expanduser().absolute()
assert artifact.exists(), f"artifact missing: {artifact}"
mode = artifact.lstat().st_mode
assert stat.S_ISREG(mode), f"artifact is not a regular file: {artifact} (mode={stat.S_IFMT(mode):o})"
assert not stat.S_ISLNK(mode), f"artifact is a symlink: {artifact}"
assert not stat.S_ISDIR(mode), f"artifact is a directory: {artifact}" Type guard
import stat
from pathlib import Path
def is_regular_nonsymlink_file(path) -> bool:
try:
mode = Path(path).lstat().st_mode
except OSError:
return False
return stat.S_ISREG(mode) and not stat.S_ISLNK(mode) Prevention
- Prompt the model to write a regular file at the exact artifact path.
- Forbid mkdir/symlink at the artifact path in the phase prompt.
When it happens
Trigger: artifact_state is None (artifact missing) or starts with 'd:'/'l:'/'s:' (directory/symlink/special) at line 208. The model created a directory where a file was expected, replaced the file with a symlink, or did not create the file at all (artifact_state None falls through to this same message).
Common situations: The model did mkdir where it should have written a file; the model symlinked the artifact to an existing doc; the artifact_key is a directory path; the model named the artifact with no extension so a tool treated it as a dir.
Related errors
- oracle stage parent changed during verification: {item.targe
- workspace snapshot root must be a real directory: {root}
- phase artifact escapes the workspace: {allowed_artifact}
- phase did not create or change its required artifact: {relat
- phase artifact must be a readable regular non-symlink file:
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/e0a74b70d82b16de.
Report an issue: GitHub.