abhigyanpatwari/GitNexus · error · ValueError
phase changed unauthorized workspace path(s): {preview}{suff
Error message
phase changed unauthorized workspace path(s): {preview}{suffix} What it means
Raised by enforce_phase_workspace (runner_artifacts.py:240) when the set of changed workspace paths (before vs after snapshot) minus the allowed set (the artifact and any new parent directories) is non-empty. The phase contract permits changing only the one declared artifact; any other change (stray file, edit outside the artifact, new directory) is unauthorized and the phase fails.
Source
Thrown at eval/workflow_bench/runner_artifacts.py:240
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:"):
allowed.add(parent_key)
parent = parent.parent
unauthorized = sorted(changed - allowed)
if unauthorized:
preview = ", ".join(unauthorized[:8])
suffix = " …" if len(unauthorized) > 8 else ""
raise ValueError(f"phase changed unauthorized workspace path(s): {preview}{suffix}")
def require_skill_fingerprint(worktree: Path, arm: str, expected: str | None, *, phase: str) -> None:
"""Fail closed when a bounded phase changes the evaluated prompt roots."""
try:
observed = skill_fingerprint(worktree, arm)
except (OSError, ValueError) as exc:
raise ValueError(f"{phase} changed the evaluated skill fingerprint") from exc
if observed != expected:
raise ValueError(f"{phase} changed the evaluated skill fingerprint")
def snapshot_plan_docs(worktree: Path) -> dict[Path, str]:
"""Hash direct, regular plan artifacts without following links."""
plans = worktree / "docs" / "plans"
if not plans.exists():View on GitHub (pinned to d540b00184)
Solutions
- Inspect the {preview} paths; remove or relocate them (the model should only touch the declared artifact).
- If a path is genuine tool noise that the model did not choose to write (like Claude Code's .cc-writes), add it to CLAUDE_BOOTSTRAP_ENTRIES or WORKSPACE_SNAPSHOT_BOOTSTRAP_NOISE per the comment at runner_artifacts.py:69-78 — extend only from an observed failure, never pre-emptively.
- Tighten the prompt/tooling so the phase writes nothing but its artifact; run builds/logs to a directory outside the hashed worktree.
Example fix
# before: a verifier writes build output into the worktree verify: npm run build # emits dist/ # after: build outside the hashed worktree verify: npm run build -- --outDir /tmp/build && cp -r /tmp/dist ./dist # or exclude dist via .gitignore if the snapshot should ignore it
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
from eval.workflow_bench.runner_artifacts import workspace_snapshot
# Take an immediate after-snapshot and diff against `before` to preview
# unauthorized changes BEFORE enforce_phase_workspace raises.
before = {...} # captured before the phase
after = workspace_snapshot(Path(worktree).resolve())
artifact_key = Path(allowed_artifact).resolve().relative_to(Path(worktree).resolve()).as_posix()
changed = {p for p in before.keys() | after.keys() if before.get(p) != after.get(p)}
unauthorized = sorted(changed - {artifact_key})
assert not unauthorized, f"phase will be rejected for unauthorized changes: {unauthorized[:8]}" Prevention
- Constrain the phase prompt to touch only the declared artifact.
- If a path is tool noise the model did not choose to write, extend CLAUDE_BOOTSTRAP_ENTRIES / WORKSPACE_SNAPSHOT_BOOTSTRAP_NOISE from the observed failure (never pre-emptively).
- Run builds/logs outside the hashed worktree.
When it happens
Trigger: sorted(changed - allowed) is non-empty at line 237. The model wrote files outside the artifact path, left temp files, edited unrelated tracked files, or a tool it invoked created build outputs/logs in the worktree. The message lists up to 8 offending paths with a trailing ellipsis if more.
Common situations: The model edited source files during a planning-only phase; a verifier/build wrote artifacts into the worktree; the model left .log/.tmp files; Claude Code's bootstrap noise was not fully covered by the exclusion set (extend WORKSPACE_SNAPSHOT_BOOTSTRAP_NOISE / CLAUDE_BOOTSTRAP_ENTRIES from an observed failure).
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- phase artifact escapes the workspace: {allowed_artifact}
- phase artifact must be a regular non-symlink file: {relative
- {phase} changed the evaluated skill fingerprint
- Analyzer build or dependency runtime changed during analysis
- Search index build failed with an integrity error and the an
AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12).
Data as JSON: /api/errors/970c4632f6134919.
Report an issue: GitHub.