paperclipai/paperclip · error · Error

ACPX package snapshot exceeds its byte bound

Error message

ACPX package snapshot exceeds its byte bound

What it means

copy() accumulates the byte size of every regular file it copies and enforces MAX_PACKAGE_SNAPSHOT_BYTES across the whole snapshot. When the cumulative total exceeds that bound it throws, refusing to build a snapshot larger than the configured memory/disk budget. Each individual file is separately capped at 16 MiB, so this fires on aggregate volume.

Source

Thrown at packages/paperclip-runner/src/drivers/acpx/private-snapshot.ts:123

      if (mapped) await symlink(mapped, target);
      return;
    }
    if (!within(root, await realpath(source)))
      throw new Error("ACPX snapshot escaped its package");
    if (before.isDirectory()) {
      await mkdir(target, { mode: 0o700 });
      directories.push(target);
      for (const entry of await readdir(source))
        await copy(join(source, entry), join(target, entry), root);
      if (!same(before, await lstat(source, { bigint: true })))
        throw new Error("ACPX package directory changed during snapshot");
      return;
    }
    if (!before.isFile() || before.size > 16n * 1024n * 1024n)
      throw new Error("ACPX module must be a bounded regular file");
    bytesCopied += Number(before.size);
    if (bytesCopied > MAX_PACKAGE_SNAPSHOT_BYTES)
      throw new Error("ACPX package snapshot exceeds its byte bound");
    const handle = await open(
      source,
      constants.O_RDONLY | constants.O_NOFOLLOW,
    );
    try {
      if (!same(before, await handle.stat({ bigint: true })))
        throw new Error("ACPX module changed before snapshot");
      const bytes = await readSnapshotBytes(handle, Number(before.size));
      if (
        !same(before, await handle.stat({ bigint: true })) ||
        !same(before, await lstat(source, { bigint: true }))
      ) {
        throw new Error("ACPX module changed during snapshot");
      }
      await writeFile(target, bytes, { flag: "wx", mode: 0o400 });
      digests[target] = digest(bytes);
    } finally {
      await handle.close();

View on GitHub (pinned to 01ad858492)

Solutions

  1. Reduce the package root to the minimal package directory actually needed for import
  2. Purge generated/binary assets from the root before snapshotting
  3. Check whether a dependency upgrade grew the tree; pin the smaller version
  4. Coordinate with the runner maintainers to raise MAX_PACKAGE_SNAPSHOT_BYTES if the workload legitimately needs it

Example fix

// before
roots: [repoRoot]
// after: admit only the needed package
roots: [join(repoRoot, 'packages/util')]
Defensive patterns

Strategy: validation

Validate before calling

let total = 0;
for (const f of await walkFiles(root)) {
  total += (await fs.stat(f)).size;
}
if (total > MAX_PACKAGE_SNAPSHOT_BYTES) throw new Error(`package bytes ${total} exceed snapshot budget`);

Try / catch

try {
  await createAcpxPrivateSnapshot({ roots: [root] });
} catch (e) {
  if (e.message.includes('exceeds its byte bound')) {
    throw new Error(`Reduce package ${root} below the snapshot byte budget or split roots`);
  }
  throw e;
}

Prevention

When it happens

Trigger: createAcpxPrivateSnapshot over a package root whose total regular-file bytes exceed MAX_PACKAGE_SNAPSHOT_BYTES; the failure can occur partway through the walk, after many files have already been copied.

Common situations: Admitting a data-heavy package (fixtures, media, generated bundles); accidentally snapshotting node_modules or a monorepo root as a single package; a dependency that ballooned after a version upgrade.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


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