{"record":{"id":"cc48ea38906393b7","repo":"abhigyanpatwari/GitNexus","slug":"sandbox-copy-path-changed-or-is-unreadable-relat","errorCode":null,"errorMessage":"sandbox_copy path changed or is unreadable: {relative}: {exc}","messagePattern":"sandbox_copy path changed or is unreadable: (.+?): (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"critical","filePath":"eval/workflow_bench/task_assets.py","lineNumber":643,"sourceCode":"    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,\n        stat.S_IFMT(opened.st_mode),\n    ) != (\n        metadata.st_dev,\n        metadata.st_ino,\n        stat.S_IFMT(metadata.st_mode),\n    ):\n        os.close(descriptor)\n        raise SandboxError(f\"sandbox_copy path changed while opening: {relative}\")\n    return descriptor\n\n","sourceCodeStart":625,"sourceCodeEnd":661,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/task_assets.py#L625-L661","documentation":"TOCTOU guard in _open_child: os.stat succeeded but the subsequent os.open(name, flags, dir_fd=<parent>) raised OSError. The chained exc is typically ENOENT (file deleted between stat and open), EACCES (permissions changed), or ENFILE/EMFILE (fd exhaustion).","triggerScenarios":"Another process mutates the worktree during snapshot capture; concurrent git checkout; antivirus/scanner holding or deleting files; the process hit its file-descriptor limit mid-walk.","commonSituations":"Two benchmark arms against the same worktree simultaneously; background indexers rewriting the tree; very deep trees exhausting the fd soft limit.","solutions":["Ensure no other process mutates the repo during snapshot capture (one worktree per concurrent arm)","Raise the fd soft limit: `ulimit -n 1048576`","Re-run capture; a transient race is the most common cause","If persistent, audit concurrent writers with lsof or fatrace"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import resource\n# raise the fd soft limit before any snapshot capture\nsoft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)\nresource.setrlimit(resource.RLIMIT_NOFILE, (min(hard, 1<<20), hard))","typeGuard":null,"tryCatchPattern":"from eval.workflow_bench.proposer_sandbox import SandboxError\n\nattempts = 0\nwhile True:\n    try:\n        snapshot = cache.prepare(task, repo=repo, resolved_sha=sha); break\n    except SandboxError as exc:\n        if \"path changed or is unreadable\" in str(exc) and attempts < 2:\n            attempts += 1   # transient TOCTOU race; retry with backoff\n            continue\n        raise","preventionTips":["Raise the file-descriptor soft limit before capture (`ulimit -n 1048576`)","Use one private worktree per concurrent arm so no cross-arm mutation occurs","Quiesce background indexers and scanners before capture"],"tags":["sandbox","toctou","race","file-descriptors"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}