abhigyanpatwari/GitNexus · error · ValueError

oracle stage parent must be a real directory: {item.target}

Error message

oracle stage parent must be a real directory: {item.target}

What it means

Inside _write_stage_file (used by staged_task_oracle to materialize oracle files AFTER the model exits): the harness mkdir's the destination parents, then walks each parent with lstat to confirm it is a real, non-symlink directory before writing the oracle file read-only. A symlink/non-dir parent would indicate a TOCTOU swap (an adversarial model process trying to redirect the oracle write outside the stage root).

Source

Thrown at eval/workflow_bench/oracle_assets.py:470

    parents = _git_checked(root, ["show", "-s", "--format=%P", "HEAD"], timeout=60)
    if parents:
        raise ValueError("oracle sanitization snapshot unexpectedly retained parent history")
    if _git_checked(root, ["remote"], timeout=60):
        raise ValueError("oracle sanitization retained a repository remote")
    if logs.exists() or logs.is_symlink():
        raise ValueError("oracle sanitization retained reflog metadata")
    return sanitized_head


def _write_stage_file(stage_root: Path, item: OracleFileSnapshot) -> None:
    destination = stage_root.joinpath(*PurePosixPath(item.target).parts)
    destination.parent.mkdir(parents=True, mode=0o700, exist_ok=True)
    current = stage_root
    for part in PurePosixPath(item.target).parts[:-1]:
        current /= part
        metadata = current.lstat()
        if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
            raise ValueError(f"oracle stage parent must be a real directory: {item.target}")
        current.chmod(0o700)
    descriptor = os.open(
        destination,
        os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, "O_NOFOLLOW", 0),
        0o400,
    )
    try:
        view = memoryview(item.payload)
        while view:
            written = os.write(descriptor, view)
            if written <= 0:
                raise OSError("short write while staging oracle")
            view = view[written:]
        os.fchmod(descriptor, 0o400)
        os.fsync(descriptor)
    finally:
        os.close(descriptor)

View on GitHub (pinned to d540b00184)

Solutions

  1. Ensure staged_task_oracle's worktree is private to a single task and not writable by the model process.
  2. Inspect `.wfbench-oracle-*/` lineage for symlinks: `find <worktree>/.wfbench-oracle-* -type l`.
  3. Validate item.target does not traverse into existing file paths; ensure the stage root is created fresh each run.
Defensive patterns

Strategy: validation

Validate before calling

import stat
from pathlib import Path, PurePosixPath

def stage_parents_are_real(stage_root: Path, target: str) -> bool:
    cur = stage_root
    for part in PurePosixPath(target).parts[:-1]:
        cur = cur / part
        try:
            st = cur.lstat()
        except OSError:
            return False
        if stat.S_ISLNK(st.st_mode) or not stat.S_ISDIR(st.st_mode):
            return False
    return True

Type guard

def is_unsafe_stage_parent(exc: BaseException) -> bool:
    return isinstance(exc, ValueError) and "stage parent must be a real directory" in str(exc)

Try / catch

from eval.workflow_bench import oracle_assets
try:
    with oracle_assets.staged_task_oracle(worktree, snapshot) as root:
        run_command(root)
except ValueError as exc:
    raise AbortTask(f"oracle staging failed: {exc}") from exc

Prevention

When it happens

Trigger: Triggered when, between mkdir(parents, exist_ok=True) and the parent walk, one of the parent components of item.target becomes a symlink or non-directory — e.g., a model-launched process racing to replace a directory with a symlink inside .wfbench-oracle-<hex>/.

Common situations: A model-under-test that escapes its sandbox and races the oracle staging write; a bug in target path construction producing a path that collides with an existing file; concurrent staged_task_oracle calls sharing a root.

Related errors


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