{"record":{"id":"e0a74b70d82b16de","repo":"abhigyanpatwari/GitNexus","slug":"phase-artifact-must-be-a-regular-non-symlink-file","errorCode":null,"errorMessage":"phase artifact must be a regular non-symlink file: {relative}","messagePattern":"phase artifact must be a regular non-symlink file: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/runner_artifacts.py","lineNumber":209,"sourceCode":"    *,\n    allowed_artifact: Path,\n) -> None:\n    \"\"\"Require a phase to change only its one explicit workspace artifact.\"\"\"\n\n    root = worktree.expanduser().absolute()\n    artifact = allowed_artifact.expanduser().absolute()\n    try:\n        relative = PurePosixPath(artifact.relative_to(root).as_posix())\n    except ValueError as exc:\n        raise ValueError(f\"phase artifact escapes the workspace: {allowed_artifact}\") from exc\n    after = workspace_snapshot(root)\n    changed = {path for path in before.keys() | after.keys() if before.get(path) != after.get(path)}\n    artifact_key = relative.as_posix()\n    artifact_state = after.get(artifact_key)\n    if before.get(artifact_key) == artifact_state:\n        raise ValueError(f\"phase did not create or change its required artifact: {relative}\")\n    if artifact_state is None or not artifact_state.startswith(\"f:\"):\n        raise ValueError(f\"phase artifact must be a regular non-symlink file: {relative}\")\n\n    try:\n        metadata = artifact.lstat()\n        descriptor = os.open(artifact, os.O_RDONLY | getattr(os, \"O_NOFOLLOW\", 0))\n    except OSError as exc:\n        raise ValueError(f\"phase artifact must be a readable regular non-symlink file: {relative}\") from exc\n    try:\n        opened = os.fstat(descriptor)\n        if (\n            stat.S_ISLNK(metadata.st_mode)\n            or not stat.S_ISREG(metadata.st_mode)\n            or not stat.S_ISREG(opened.st_mode)\n            or metadata.st_dev != opened.st_dev\n            or metadata.st_ino != opened.st_ino\n        ):\n            raise ValueError(f\"phase artifact must be a regular non-symlink file: {relative}\")\n    finally:\n        os.close(descriptor)","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/runner_artifacts.py#L191-L227","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before: model creates a directory or symlink at the artifact path\nmkdir docs/plans/plan.md   # or: ln -s ../template.md docs/plans/plan.md\n\n# after: model writes a regular file\ncat > docs/plans/plan.md <<'EOF'\n# Plan\n...\nEOF","handlingStrategy":"validation","validationCode":"import os, stat\nfrom pathlib import Path\n\nartifact = Path(allowed_artifact).expanduser().absolute()\nassert artifact.exists(), f\"artifact missing: {artifact}\"\nmode = artifact.lstat().st_mode\nassert stat.S_ISREG(mode), f\"artifact is not a regular file: {artifact} (mode={stat.S_IFMT(mode):o})\"\nassert not stat.S_ISLNK(mode), f\"artifact is a symlink: {artifact}\"\nassert not stat.S_ISDIR(mode), f\"artifact is a directory: {artifact}\"","typeGuard":"import stat\nfrom pathlib import Path\n\ndef is_regular_nonsymlink_file(path) -> bool:\n    try:\n        mode = Path(path).lstat().st_mode\n    except OSError:\n        return False\n    return stat.S_ISREG(mode) and not stat.S_ISLNK(mode)","tryCatchPattern":null,"preventionTips":["Prompt the model to write a regular file at the exact artifact path.","Forbid mkdir/symlink at the artifact path in the phase prompt."],"tags":["phase-boundary","artifact","symlink","integrity"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}