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.

Solutions

  1. Run the snapshot only after package installation has fully completed and the tree is idle
  2. Retry the snapshot — the identity check is designed so a quiet re-run succeeds
  3. Exclude the package root from background sync/scan tools during runs
  4. 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

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


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/6726fda41caf7a54. Report an issue: GitHub.

Appendix: 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)