{"record":{"id":"03c267dd89e806db","repo":"abhigyanpatwari/GitNexus","slug":"compound-engineering-plugin-file-changed-while-bei","errorCode":null,"errorMessage":"Compound Engineering plugin file changed while being copied: {path}","messagePattern":"Compound Engineering plugin file changed while being copied: (.+?)","errorType":"exception","errorClass":"SandboxError","httpStatus":null,"severity":"critical","filePath":"eval/workflow_bench/runtime_mounts.py","lineNumber":381,"sourceCode":"            raise SandboxError(f\"Compound Engineering plugin file changed during validation: {path}\")\n        chunks: list[bytes] = []\n        remaining = MAX_CE_PLUGIN_FILE_BYTES + 1\n        while remaining > 0:\n            chunk = os.read(descriptor, min(64 * 1024, remaining))\n            if not chunk:\n                break\n            chunks.append(chunk)\n            remaining -= len(chunk)\n        payload = b\"\".join(chunks)\n        after = os.fstat(descriptor)\n    finally:\n        os.close(descriptor)\n    if len(payload) > MAX_CE_PLUGIN_FILE_BYTES:\n        raise SandboxError(f\"Compound Engineering plugin file exceeds the per-file limit: {path}\")\n    identity_before = (opened.st_dev, opened.st_ino, opened.st_size, opened.st_mtime_ns)\n    identity_after = (after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns)\n    if identity_after != identity_before or len(payload) != after.st_size:\n        raise SandboxError(f\"Compound Engineering plugin file changed while being copied: {path}\")\n    return payload, bool(before.st_mode & 0o111)\n\n\ndef _write_snapshot_file(path: Path, payload: bytes, *, executable: bool) -> None:\n    path.parent.mkdir(parents=True, exist_ok=True)\n    descriptor = os.open(\n        path,\n        os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, \"O_NOFOLLOW\", 0),\n        0o500 if executable else 0o400,\n    )\n    try:\n        view = memoryview(payload)\n        while view:\n            written = os.write(descriptor, view)\n            view = view[written:]\n        os.fchmod(descriptor, 0o555 if executable else 0o444)\n    finally:\n        os.close(descriptor)","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/eval/workflow_bench/runtime_mounts.py#L363-L399","documentation":"Final stability check in _bounded_plugin_bytes. After the read completes, the (dev, ino, size, mtime_ns) tuple from the post-open fstat is compared to the post-read fstat, and the payload length is checked against the final size. Any drift means the file was modified mid-copy, so the bytes read cannot be trusted as the plugin's stable content.","triggerScenarios":"The file was rewritten (size, mtime, or inode changed) between the start and end of the read loop; the writer did not just append but replaced the inode (atomic rename) or truncated and rewrote.","commonSituations":"Editor save during snapshot; 'npm install' rewriting package files in place; a build tool using atomic rename to publish into scripts/; rsync running against the same tree.","solutions":["Quiesce all writers against the plugin source, then re-run (a stable tree makes this check pass deterministically).","Snapshot the plugin to an immutable location (cp -a into a fresh dir, chmod -R a-w) and pass that as --ce-plugin-dir.","Run the benchmark against a released tarball extracted into a throwaway directory rather than a live checkout.","If you control the writer, have it write to a temp path and atomic-rename once, outside the snapshot window."],"exampleFix":"# before\nwfbench run --ce-plugin-dir ./ce-plugin   # live checkout, editor open\n# after\ngit -C ./ce-plugin archive --prefix=ce-plugin-frozen/ HEAD | tar -x -C /tmp\nwfbench run --ce-plugin-dir /tmp/ce-plugin-frozen","handlingStrategy":"validation","validationCode":"from pathlib import Path\nimport shutil, os, stat\n\nsrc = Path(\"ce-plugin\")\nfrozen = Path(\"/tmp/ce-plugin-frozen\")\nif frozen.exists():\n    shutil.rmtree(frozen)\nshutil.copytree(src, frozen, symlinks=False)\n# make immutable so mtime cannot change mid-snapshot\nfor root, _, files in os.walk(frozen):\n    Path(root).chmod(0o555)\n    for f in files:\n        Path(root, f).chmod(0o444)\n# pass frozen as --ce-plugin-dir","typeGuard":"import os\nfrom pathlib import Path\n\ndef is_stable_during_read(path: Path) -> bool:\n    try:\n        before = path.lstat()\n        fd = os.open(path, os.O_RDONLY | getattr(os, \"O_NOFOLLOW\", 0))\n        a = os.fstat(fd)\n        data = b\"\"\n        while True:\n            chunk = os.read(fd, 64 * 1024)\n            if not chunk:\n                break\n            data += chunk\n        b = os.fstat(fd)\n        os.close(fd)\n    except OSError:\n        return False\n    return (a.st_size, a.st_mtime_ns) == (b.st_size, b.st_mtime_ns) and len(data) == b.st_size","tryCatchPattern":"try:\n    snapshot = _build_ce_plugin_snapshot(config, destination_parent)\nexcept SandboxError as exc:\n    if \"changed while being copied\" in str(exc):\n        log.error(\"plugin file modified mid-copy; freeze source and retry\")\n    raise","preventionTips":["Freeze the plugin into an immutable copy before benchmarking.","Close editors and stop watchers touching the plugin tree.","Run against a released tarball extracted fresh per run."],"tags":["sandbox","ce-plugin","toctou","race-condition","content-integrity","workflow-bench"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}