paperclipai/paperclip · error · Error

ACPX package snapshot exceeds its file bound

Error message

ACPX package snapshot exceeds its file bound

What it means

The recursive copy inside createAcpxPrivateSnapshot enforces a hard cap of 30,000 files per package snapshot. When the number of copied files exceeds this bound the library aborts with this error, refusing to snapshot an unbounded package tree. This is a deliberate resource-exhaustion guard, not an I/O failure.

Source

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

    await rm(directory, { recursive: true, force: true });
  };
  const mapPath = (source: string): string | null => {
    const candidates = sourceRoots
      .map((root, index) => ({ root, index }))
      .filter(({ root }) => within(root, source))
      .sort((a, b) => b.root.length - a.root.length);
    const match = candidates[0];
    return match
      ? resolve(roots[match.index]!, relative(match.root, source))
      : null;
  };
  const copy = async (
    source: string,
    target: string,
    root: string,
  ): Promise<void> => {
    if (++filesCopied > 30_000)
      throw new Error("ACPX package snapshot exceeds its file bound");
    const before = await lstat(source, { bigint: true });
    if (before.isSymbolicLink()) {
      const canonical = await realpath(source);
      const mapped = mapPath(canonical);
      // Package-manager links to unqualified packages do not grant import authority.
      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;

View on GitHub (pinned to 01ad858492)

Solutions

  1. Point the package root at the specific package directory, not a parent containing node_modules or build output
  2. Exclude generated artifacts, fixtures, and vendored trees from the package root before snapshotting
  3. Split the dependency into smaller admitted package roots if legitimately huge
  4. If you maintain the runner and truly need more files, raise the 30_000 bound consciously, accepting the resource cost

Example fix

// before
roots: [workspaceRoot]
// after
roots: [join(workspaceRoot, 'packages/my-app')]
Defensive patterns

Strategy: validation

Validate before calling

async function countFiles(root: string): Promise<number> {
  let n = 0;
  const stack = [root];
  while (stack.length) {
    const dir = stack.pop()!;
    for (const e of await fs.readdir(dir, { withFileTypes: true })) {
      if (++n > 30_000) throw new Error(`package too large: ${n} files`);
      if (e.isDirectory()) stack.push(path.join(dir, e.name));
    }
  }
  return n;
}

Try / catch

try {
  await createAcpxPrivateSnapshot({ roots: [root] });
} catch (e) {
  if (e.message.includes('exceeds its file bound')) {
    throw new Error(`Admitted root ${root} has >30k files; narrow the root`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling createAcpxPrivateSnapshot on a package root whose recursive file count exceeds 30,000 (filesCopied counter increments on every copy invocation, including nested directories and symlinks).

Common situations: Admitting an oversized monorepo package (e.g. a bundled node_modules inside the package, test fixtures, or generated assets) as an ACPX package root; accidentally pointing the snapshot at node_modules instead of a single package.

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/21c7ed1ffadb3dcf. Report an issue: GitHub.