abhigyanpatwari/GitNexus · error · SandboxError

{label} file target has the wrong type: {relative}

Error message

{label} file target has the wrong type: {relative}

What it means

Raised by _prepare_clone_target when the caller asked for a file target (directory=False), the leaf exists, but it is not a regular file (e.g. it is a directory, socket, or device). The harness creates the file only when absent (O_CREAT|O_EXCL|O_NOFOLLOW) and otherwise requires an existing regular file; any other type is rejected to avoid writing through a non-file node.

Source

Thrown at eval/workflow_bench/proposer_sandbox.py:624

        leaf = relative.parts[-1]
        try:
            mode = os.stat(leaf, dir_fd=current_fd, follow_symlinks=False).st_mode
        except FileNotFoundError:
            mode = None
        if mode is not None and stat.S_ISLNK(mode):
            raise SandboxError(f"{label} target cannot be a symlink: {relative}")
        if directory is True:
            if mode is None:
                os.mkdir(leaf, mode=0o700, dir_fd=current_fd)
            elif not stat.S_ISDIR(mode):
                raise SandboxError(f"{label} directory target has the wrong type: {relative}")
        elif directory is False:
            if mode is None:
                file_flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | nofollow
                file_fd = os.open(leaf, file_flags, 0o600, dir_fd=current_fd)
                os.close(file_fd)
            elif not stat.S_ISREG(mode):
                raise SandboxError(f"{label} file target has the wrong type: {relative}")
        elif mode is not None and not (stat.S_ISREG(mode) or stat.S_ISDIR(mode)):
            raise SandboxError(f"{label} target has the wrong type: {relative}")
    finally:
        os.close(current_fd)
    return clone / Path(*relative.parts)


def stage_task_assets(
    task: Mapping[str, Any],
    *,
    repo: Path,
    clone: Path,
) -> list[ReadOnlyMount]:
    """Compatibility wrapper for immutable task-asset staging."""

    # Kept lazy to avoid a module cycle: task_assets uses the sandbox's
    # shared error, mount, and no-follow target primitives.
    from .task_assets import stage_task_assets as stage_immutable_task_assets

View on GitHub (pinned to d540b00184)

Solutions

  1. Check the leaf type: `stat -c '%F' <worktree>/<leaf>`.
  2. If a directory is colliding, remove it from the repo or move the file target to a path not occupied by a directory.
  3. Use a clean worktree so generated state from earlier runs cannot collide.
  4. Align the task's declared target type (file vs directory) with the real on-disk type at that path.

Example fix

// before — file target collides with a tracked directory
target: eval/marker   # directory=False, but eval/marker is a dir
// after — point the file target elsewhere or remove the directory
git rm -r eval/marker
Defensive patterns

Strategy: validation

Validate before calling

import os, stat
from pathlib import PurePosixPath

def file_target_ok(clone: Path, relative: str) -> bool:
    parts = PurePosixPath(relative).parts
    pfd = os.open(clone, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
    try:
        for p in parts[:-1]:
            nxt = os.open(p, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=pfd)
            os.close(pfd); pfd = nxt
        try:
            mode = os.stat(parts[-1], dir_fd=pfd, follow_symlinks=False).st_mode
            return stat.S_ISREG(mode)
        except FileNotFoundError:
            return True  # will be created
    finally:
        os.close(pfd)

Try / catch

from .proposer_sandbox import SandboxError

try:
    target = _prepare_clone_target(clone, PurePosixPath(rel), directory=False, label='file')
except SandboxError:
    # leaf exists but is not a regular file; clear or reroute
    raise

Prevention

When it happens

Trigger: _prepare_clone_target(..., directory=False) finds an existing leaf whose mode fails stat.S_ISREG — for instance a task declares a file-shaped target but a directory already occupies that path.

Common situations: A task target path was previously used as a directory and is now declared as a file (or vice versa); leftover state from a prior run; a tracked directory sits where a file mount placeholder is wanted.

Related errors


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