{"record":{"id":"26ade66c46fb86d3","repo":"abhigyanpatwari/GitNexus","slug":"sandbox-copy-accepts-only-regular-files-and-direct","errorCode":null,"errorMessage":"sandbox_copy accepts only regular files and directories: {relative}","messagePattern":"sandbox_copy accepts only regular files and directories: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"error","filePath":"eval/workflow_bench/task_assets.py","lineNumber":413,"sourceCode":"            for name in names:\n                child_relative = relative / name\n                child_metadata = os.stat(name, dir_fd=descriptor, follow_symlinks=False)\n                if stat.S_ISLNK(child_metadata.st_mode):\n                    if not self.allow_symlinks:\n                        raise SandboxError(f\"sandbox_copy must not traverse a symlink: {child_relative}\")\n                    self._copy_symlink(descriptor, name, child_relative, child_metadata)\n                    continue\n                child = _open_child(descriptor, name, child_relative)\n                try:\n                    self.copy_descriptor(child, child_relative)\n                finally:\n                    os.close(child)\n            after = os.fstat(descriptor)\n            if _mutation_identity(before) != _mutation_identity(after):\n                raise SandboxError(f\"sandbox_copy directory changed while snapshotting: {relative}\")\n            return\n        if not stat.S_ISREG(before.st_mode):\n            raise SandboxError(f\"sandbox_copy accepts only regular files and directories: {relative}\")\n        self._copy_file(descriptor, relative, before)\n\n    def _record_directory(self, relative: PurePosixPath) -> None:\n        self._ensure_parents(relative.parent)\n        self._record(AssetManifestEntry(path=relative, kind=\"directory\"))\n        destination = self.destination / Path(*relative.parts)\n        destination.mkdir(mode=0o700, exist_ok=True)\n\n    def _copy_file(self, descriptor: int, relative: PurePosixPath, before: os.stat_result) -> None:\n        self._ensure_parents(relative.parent)\n        if self.budget.total_bytes + before.st_size > MAX_TASK_ASSET_BYTES:\n            raise SandboxError(\"sandbox_copy exceeds the total byte limit\")\n        destination = self.destination / Path(*relative.parts)\n        flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, \"O_CLOEXEC\", 0)\n        output = os.open(destination, flags, 0o600)\n        digest = hashlib.sha256()\n        copied = 0\n        try:","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/task_assets.py#L395-L431","documentation":"Raised by _SnapshotBuilder.copy_descriptor when a declared sandbox_copy path resolves to a filesystem entry that is neither a regular file nor a directory (e.g. a FIFO, socket, character/block device, or other special file). The snapshot pipeline can only freeze regular files and directories because it reflinks or buffered-copies bytes and records a sha256; special files have no stable byte content to capture. This guard fires after the directory branch and just before _copy_file, so it is the final type gate on the descriptor that was opened with O_NOFOLLOW.","triggerScenarios":"A task declares sandbox_copy of a path that contains a Unix special file: `os.mkfifo`, `socket.socket(AF_UNIX).bind(...)`, or a device node under `/dev` bind-mounted into the repo. Also triggered when a regular file is replaced with a special file between the _open_child stat and the fstat in copy_descriptor (a TOCTOU swap), though that path is more commonly hit via the 'changed while snapshotting' guards.","commonSituations":"Accidentally declaring a sandbox_copy root that includes a build artifact directory containing named pipes or sockets (e.g. leftover `.npm/_cacache` locks, postgres test harness sockets, X11-style `/tmp/.X11-unix` binds). Declaring a path that a previous test run left a FIFO in. Container setups where the repo is overlaid with device nodes.","solutions":["Inspect the declared path with `find <path> -type p -o -type s -o -type b -o -type c` and remove or exclude the offending special files from the declaration.","Narrow the sandbox_copy declaration to the specific subdirectories or files you need instead of a broad root that sweeps in sockets/pipes.","If the special file is a legitimate runtime artifact, ensure it is created inside the arm clone at runtime, not captured in the immutable snapshot.","Re-run the benchmark after cleaning the repo working tree (`git clean -fdx`) to eliminate stray special files left by prior tooling."],"exampleFix":"// before (task definition)\n{\"sandbox_copy\": [\"repo/build\"]}  // build/ contains a leftover FIFO\n\n// after\n{\"sandbox_copy\": [\"repo/build/dist\", \"repo/build/config.json\"]}\n// or remove the FIFO: rm -f repo/build/.lock-fifo","handlingStrategy":"validation","validationCode":"from pathlib import Path, PurePosixPath\nimport stat, os\n\ndef validate_no_special_files(repo: Path, declarations: list[str]) -> None:\n    for raw in declarations:\n        root = repo / raw\n        for current, dirs, files in os.walk(root, followlinks=False):\n            for name in files:\n                p = Path(current) / name\n                mode = p.lstat().st_mode\n                if not stat.S_ISREG(mode):\n                    raise ValueError(f\"non-regular file in sandbox_copy: {p} (mode {oct(stat.S_IFMT(mode))})\")\n            for name in dirs:\n                p = Path(current) / name\n                if not stat.S_ISDIR(p.lstat().st_mode):\n                    raise ValueError(f\"non-directory entry in sandbox_copy dir list: {p}\")\n\n# run before TaskAssetCache.prepare\nvalidate_no_special_files(repo_path, task[\"sandbox_copy\"])","typeGuard":"import stat\nfrom pathlib import Path\n\ndef is_regular_or_dir(p: Path) -> bool:\n    m = p.lstat().st_mode\n    return stat.S_ISREG(m) or stat.S_ISDIR(m)","tryCatchPattern":"from eval.workflow_bench.propposer_sandbox import SandboxError\n\ntry:\n    snapshot = cache.prepare(task, repo=repo, resolved_sha=sha)\nexcept SandboxError as exc:\n    if \"accepts only regular files\" in str(exc):\n        # scan and clean the declared tree, then re-run\n        ...\n    raise","preventionTips":["Run `git clean -fdx` on the repo before capture to remove stray sockets, pipes, and device nodes left by prior tooling.","Avoid declaring broad roots; declare the specific files and directories the task reads.","Do not run database servers, X servers, or socket-creating daemons with runtimes inside declared sandbox_copy paths."],"tags":["sandbox","filesystem","validation","special-files"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}