abhigyanpatwari/GitNexus · error · ValueError

phase artifact must be a readable regular non-symlink file:

Error message

phase artifact must be a readable regular non-symlink file: {relative}

What it means

Raised by enforce_phase_workspace (runner_artifacts.py:215) when os.open(artifact, O_RDONLY|O_NOFOLLOW) raises OSError — the artifact cannot be opened for reading. This is the readable-file guard before the TOCTOU check; an unreadable artifact (permissions, ACL, special file) cannot be verified and is rejected.

Source

Thrown at eval/workflow_bench/runner_artifacts.py:215

    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)

    allowed = {artifact_key}
    parent = relative.parent
    while parent.parts:
        parent_key = parent.as_posix()
        if parent_key not in before and after.get(parent_key, "").startswith("d:"):

View on GitHub (pinned to d540b00184)

Solutions

  1. Check the file's permissions and ownership (ls -l <artifact>); chmod +r or chown so the benchmark user can read it.
  2. Ensure the artifact is a real file, not a symlink — re-run the phase with a prompt that writes a regular file.
  3. On locked-down systems, inspect audit logs (ausearch -m AVC) for a denial on the open.

Example fix

# before: model writes the artifact unreadable
with open(artifact, 'w') as f: f.write(plan)
os.chmod(artifact, 0o000)

# after: leave it world-readable
with open(artifact, 'w') as f: f.write(plan)
os.chmod(artifact, 0o644)
Defensive patterns

Strategy: validation

Validate before calling

import os
from pathlib import Path

artifact = Path(allowed_artifact).expanduser().absolute()
assert artifact.exists(), f"artifact missing: {artifact}"
assert os.access(artifact, os.R_OK), f"artifact not readable: {artifact}"
# O_NOFOLLOW will fail on a symlink — reject up front
assert not artifact.is_symlink(), f"artifact is a symlink: {artifact}"

Prevention

When it happens

Trigger: artifact.lstat() succeeds but os.open fails: the file mode lacks read permission for the benchmark user, an ACL denies open, or the path is a special file (FIFO/device) that cannot be O_RDONLY-opened in the expected way. O_NOFOLLOW also fails with ELOOP if the path is a symlink.

Common situations: The model writes the artifact chmod 000 or 600 owned by another user; an SELinux/AppArmor policy denies the read; the artifact path is actually a symlink (O_NOFOLLOW ELOOP); the file is on a read-only mount that became inaccessible.

Related errors


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