paperclipai/paperclip · error · Error
ACPX package root changed during snapshot
Error message
ACPX package root changed during snapshot
What it means
After copying all roots, createAcpxPrivateSnapshot re-stats each admitted package root and compares it with the identity captured before the walk. If a root's identity changed during the snapshot — the package directory itself was replaced, renamed, or mutated — it throws. This is the top-level guard that the whole snapshot corresponds to one stable point in time.
Source
Thrown at packages/paperclip-runner/src/drivers/acpx/private-snapshot.ts:153
) {
throw new Error("ACPX module changed during snapshot");
}
await writeFile(target, bytes, { flag: "wx", mode: 0o400 });
digests[target] = digest(bytes);
} finally {
await handle.close();
}
};
try {
for (let index = 0; index < sourceRoots.length; index++) {
await copy(sourceRoots[index]!, roots[index]!, sourceRoots[index]!);
if (
!same(
sourceIdentities[index]!,
await lstat(sourceRoots[index]!, { bigint: true }),
)
) {
throw new Error("ACPX package root changed during snapshot");
}
}
// Supply bare-package lookup links only for already admitted package roots.
const packages: Array<{ name: string; root: string }> = [];
for (const root of roots) {
const file = await open(join(root, "package.json")).catch(() => null);
if (!file) continue;
try {
const metadata = JSON.parse(await file.readFile("utf8"));
if (
typeof metadata.name === "string" &&
/^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+$/i.test(metadata.name)
) {
packages.push({ name: metadata.name, root });
}
} finally {
await file.close();
}View on GitHub (pinned to 01ad858492)
Solutions
- Run the snapshot only after package installation has fully completed and the tree is idle
- Retry the snapshot — the identity check is designed so a quiet re-run succeeds
- Exclude the package root from background sync/scan tools during runs
- Lock dependency installs so no concurrent process replaces package directories
Example fix
// before
spawn('npm', ['install']);
await snapshot(roots);
// after
execSync('npm install');
await snapshot(roots); Defensive patterns
Strategy: retry
Validate before calling
const ids = [];
for (const root of roots) ids.push(await fs.stat(root));
await new Promise(r => setTimeout(r, 300));
for (let i = 0; i < roots.length; i++) {
const now = await fs.stat(roots[i]);
if (now.ino !== ids[i].ino || now.mtimeMs !== ids[i].mtimeMs) throw new Error(`root changing: ${roots[i]}`);
} Try / catch
try {
await createAcpxPrivateSnapshot({ roots });
} catch (e) {
if (e.message.includes('package root changed during snapshot')) {
await waitForInstallsToFinish();
await createAcpxPrivateSnapshot({ roots });
} else throw e;
} Prevention
- Serialize installs and snapshot creation — never overlap them
- Pause file-sync tools (rsync, cloud sync) over the workspace during runs
- Pin dependency versions so package directories are not replaced mid-run
When it happens
Trigger: createAcpxPrivateSnapshot (called by privateSnapshot/snapshot) finishes copying roots, and the final lstat of any sourceRoots[i] no longer matches the sourceIdentities[i] captured at the start — e.g. the package directory was deleted, re-created by an install, or had entries added/removed during the walk.
Common situations: `npm install`/`pnpm install` rebuilding node_modules while a session starts; a sync tool (Dropbox, rsync job) mutating the workspace; a CI cache restore finishing mid-snapshot.
Related errors
- ACPX file ended during snapshot
- ACPX package directory changed during snapshot
- ACPX module changed before snapshot
- ACPX module changed during snapshot
- ACPX ${agent} runtime executable changed while it was verifi
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/6726fda41caf7a54.
Report an issue: GitHub.