{"record":{"id":"36ce2fcfa315d4b6","repo":"abhigyanpatwari/GitNexus","slug":"target-controlled-name-must-not-be-a-directory","errorCode":null,"errorMessage":"target-controlled {name} must not be a directory","messagePattern":"target-controlled (.+?) must not be a directory","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/sanitized_graph.py","lineNumber":114,"sourceCode":"        raise SandboxError(\"sandbox_dependencies must be a list\")\n    for item in dependencies:\n        if not isinstance(item, Mapping):\n            continue\n        for field in (\"source\", \"target\"):\n            value = item.get(field)\n            if isinstance(value, str) and _is_restricted_path(value):\n                raise SandboxError(f\"sandbox dependency cannot expose prebuilt graph or harness data: {value}\")\n\n\ndef _replace_control_file(root: Path, name: str, payload: bytes) -> None:\n    path = root / name\n    try:\n        metadata = path.lstat()\n    except FileNotFoundError:\n        metadata = None\n    if metadata is not None:\n        if stat.S_ISDIR(metadata.st_mode):\n            raise SandboxError(f\"target-controlled {name} must not be a directory\")\n        path.unlink()\n    descriptor = os.open(\n        path,\n        os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, \"O_NOFOLLOW\", 0),\n        0o600,\n    )\n    try:\n        view = memoryview(payload)\n        while view:\n            written = os.write(descriptor, view)\n            if written <= 0:\n                raise OSError(f\"short write while neutralizing {name}\")\n            view = view[written:]\n        os.fsync(descriptor)\n    finally:\n        os.close(descriptor)\n\n","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/sanitized_graph.py#L96-L132","documentation":"Precondition guard in _replace_control_file. Before overwriting .gitnexusrc or .gitnexusignore to neutralize target index inputs, the helper lstat's the path; if it currently exists as a directory, the harness refuses (it cannot atomically replace a directory with a regular file via O_CREAT|O_EXCL). This protects the neutralization step from an inconsistent starting state.","triggerScenarios":"_neutralize_target_index_inputs calls _replace_control_file for .gitnexusrc or .gitnexusignore, and one of them is a directory in the sanitized clone root. The error message interpolates the offending control-file name.","commonSituations":"A task spec or fixture created .gitnexusrc/ as a directory (e.g. to hold snippets); a symlinked config layout resolved to a directory; a previous failed run left a partial directory behind.","solutions":["Inspect the clone root: 'ls -la <clone>/.gitnexusrc <clone>/.gitnexusignore' and confirm they are not directories.","Remove or convert the directory to a regular file in the task fixture so the harness can replace it.","Re-create the sanitized task snapshot after fixing the fixture so the bad state does not recur.","Check that no sandbox_copy or sandbox_dependencies entry creates one of these names as a directory."],"exampleFix":"# before (fixture builds .gitnexusrc as a dir)\nmkdir .gitnexusrc\n# after\necho '{}' > .gitnexusrc","handlingStrategy":"validation","validationCode":"from pathlib import Path\nimport stat\n\nclone = Path(\"clone-root\")\nfor name in (\".gitnexusrc\", \".gitnexusignore\"):\n    p = clone / name\n    if p.is_symlink():\n        raise SystemExit(f\"{name} is a symlink\")\n    if p.exists() and not p.is_file():\n        raise SystemExit(f\"{name} must be a regular file, not a directory\")","typeGuard":"from pathlib import Path\nimport stat\n\ndef control_file_is_writable(path: Path) -> bool:\n    try:\n        mode = path.lstat().st_mode\n    except FileNotFoundError:\n        return True\n    return not stat.S_ISDIR(mode) and not stat.S_ISLNK(mode)","tryCatchPattern":"# _replace_control_file is internal; pre-clean the clone before graph prep:\ntry:\n    _neutralize_target_index_inputs(clone_root)\nexcept SandboxError as exc:\n    if \"must not be a directory\" in str(exc):\n        log.error(\".gitnexusrc/.gitnexusignore is a dir in the fixture; convert to a file\")\n    raise","preventionTips":["Never create .gitnexusrc or .gitnexusignore as directories in fixtures.","Audit sandbox_copy entries that might create those names.","Recreate the sanitized task snapshot after fixing the fixture."],"tags":["sandbox","graph","validation","filesystem","workflow-bench","gitnexus"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}