{"record":{"id":"9fc3bec6ee51171c","repo":"abhigyanpatwari/GitNexus","slug":"sandbox-copy-parent-must-be-a-directory-relative","errorCode":null,"errorMessage":"sandbox_copy parent must be a directory: {relative}","messagePattern":"sandbox_copy parent must be a directory: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/task_assets.py","lineNumber":632,"sourceCode":"        os.close(current)\n        raise\n\n\ndef _open_child(\n    parent_descriptor: int,\n    name: str,\n    relative: PurePosixPath,\n    *,\n    require_directory: bool = False,\n) -> int:\n    try:\n        metadata = os.stat(name, dir_fd=parent_descriptor, follow_symlinks=False)\n    except OSError as exc:\n        raise SandboxError(f\"sandbox_copy path is unavailable: {relative}: {exc}\") from exc\n    if stat.S_ISLNK(metadata.st_mode):\n        raise SandboxError(f\"sandbox_copy must not traverse a symlink: {relative}\")\n    if require_directory and not stat.S_ISDIR(metadata.st_mode):\n        raise SandboxError(f\"sandbox_copy parent must be a directory: {relative}\")\n    if not (stat.S_ISDIR(metadata.st_mode) or stat.S_ISREG(metadata.st_mode)):\n        raise SandboxError(f\"sandbox_copy accepts only regular files and directories: {relative}\")\n    flags = os.O_RDONLY | getattr(os, \"O_CLOEXEC\", 0) | getattr(os, \"O_NOFOLLOW\", 0)\n    if stat.S_ISDIR(metadata.st_mode):\n        flags |= os.O_DIRECTORY\n    else:\n        flags |= getattr(os, \"O_NONBLOCK\", 0)\n    try:\n        descriptor = os.open(name, flags, dir_fd=parent_descriptor)\n    except OSError as exc:\n        raise SandboxError(f\"sandbox_copy path changed or is unreadable: {relative}: {exc}\") from exc\n    opened = os.fstat(descriptor)\n    if not (stat.S_ISDIR(opened.st_mode) or stat.S_ISREG(opened.st_mode)):\n        os.close(descriptor)\n        raise SandboxError(f\"sandbox_copy accepts only regular files and directories: {relative}\")\n    if (\n        opened.st_dev,\n        opened.st_ino,","sourceCodeStart":614,"sourceCodeEnd":650,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/task_assets.py#L614-L650","documentation":"Raised by _open_child when require_directory=True (set for every intermediate component by _open_relative) and os.stat shows the component is not a directory. This catches a regular file sitting where the walker needs to descend.","triggerScenarios":"A declaration path like src/index.ts/utils where src/index.ts is a file; an intermediate name collides with an existing regular file; a directory was renamed to a file between pins.","commonSituations":"Renaming a directory to a file (or vice versa) between commit pins; typo'd declaration path that joins a filename with further children.","solutions":["Confirm each intermediate component is a directory at the SHA: `git cat-file -e <sha>:<parent>/`","Correct the declaration path so only the final component is a file","Re-pin the task to a SHA whose layout matches the declaration"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import os\nfrom pathlib import Path\n\ndef parents_are_dirs(root: Path, declared: list[str]) -> list[str]:\n    bad = []\n    for decl in declared:\n        parts = Path(decl).parts[:-1]  # intermediate components only\n        walker = root\n        for part in parts:\n            walker = walker / part\n            try:\n                if not walker.is_dir():\n                    bad.append(f\"{decl}: {walker} is not a directory\"); break\n            except OSError as exc:\n                bad.append(f\"{decl}: {exc}\"); break\n    return bad","typeGuard":null,"tryCatchPattern":"from eval.workflow_bench.proposer_sandbox import SandboxError\n\ntry:\n    snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)\nexcept SandboxError as exc:\n    if \"parent must be a directory\" in str(exc):\n        raise SystemExit(f\"declaration path layout invalid: {exc}\") from exc\n    raise","preventionTips":["Confirm every intermediate path component is a directory at the SHA before capture","Re-pin tasks when a directory/file swap occurs in the repo layout"],"tags":["sandbox","sandbox-copy","path-layout"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}