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
- Point the package root at the specific package directory, not a parent containing node_modules or build output
- Exclude generated artifacts, fixtures, and vendored trees from the package root before snapshotting
- Split the dependency into smaller admitted package roots if legitimately huge
- 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
- Always admit the individual package directory, never node_modules or the repo root
- Prune generated assets, fixtures, and vendored trees from packaged roots
- Count files (pre-flight) before admitting a new package root
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
- ACPX module must be a bounded regular file
- ACPX package snapshot exceeds its byte bound
- ACPX runtime executable must be a bounded executable file
- ACPX file ended during snapshot
- ACPX snapshot escaped its package
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/21c7ed1ffadb3dcf.
Report an issue: GitHub.