nexu-io/open-design · error · Error
zip contains too many files
Error message
zip contains too many files
What it means
Thrown by `importClaudeDesignZip` when the number of decoded non-directory entries reaches `MAX_FILES` (5000). The check runs at the top of the per-entry loop before adding the next file, capping the total entry count to protect the daemon from zip bombs by file count.
Source
Thrown at apps/daemon/src/design/claude-design-import.ts:33
name: string;
method: number;
compressedSize: number;
uncompressedSize: number;
localOffset: number;
isDirectory: boolean;
};
type ImportedFile = { path: string; body: Buffer };
export async function importClaudeDesignZip(zipPath: string, projectDir: string) {
const zip = await readFile(zipPath);
const entries = readCentralDirectory(zip);
const files: ImportedFile[] = [];
let totalBytes = 0;
for (const entry of entries) {
if (entry.isDirectory) continue;
if (files.length >= MAX_FILES) throw new Error('zip contains too many files');
const relPath = sanitizeZipPath(entry.name);
if (entry.uncompressedSize > MAX_FILE_BYTES) {
throw new Error(`zip file too large: ${relPath}`);
}
// Decode first; the central directory's uncompressedSize is unreliable for
// streaming/data-descriptor zips (it can read 0 even when the payload
// carries real data). The inflate cap and the post-decode size checks below
// are authoritative.
const body = readEntryBody(zip, entry);
if (body.length > MAX_FILE_BYTES) {
throw new Error(`zip file too large: ${relPath}`);
}
if (entry.uncompressedSize > 0 && body.length !== entry.uncompressedSize) {
throw new Error(`zip entry size mismatch: ${relPath}`);
}
totalBytes += body.length;
if (totalBytes > MAX_TOTAL_BYTES) throw new Error('zip is too large');View on GitHub (pinned to 5be4028344)
Solutions
- Reduce the archive to fewer than 5000 file entries by removing unnecessary files (e.g. `node_modules`, build output).
- If exporting from a tool, configure it to emit only the HTML and required assets.
- Reject the upload at the request layer if the declared entry count exceeds 5000.
Defensive patterns
Strategy: validation
Validate before calling
const MAX_FILES = 5000;
async function zipEntryCountDoesNotExceed(zipPath: string): Promise<boolean> {
const zip = await readFile(zipPath);
const entries = readCentralDirectory(zip); // reuse the importer's reader
return entries.filter((e) => !e.isDirectory).length <= MAX_FILES;
}
if (!await zipEntryCountDoesNotExceed(zipPath)) {
throw new Error('zip exceeds the 5000-file cap');
} Try / catch
try {
await importClaudeDesignZip(zipPath, projectDir);
} catch (err) {
if (err instanceof Error && err.message === 'zip contains too many files') {
// trim the archive and re-upload
}
throw err;
} Prevention
- Exclude `node_modules`, build output, and other bulky trees before zipping.
- Reject uploads whose declared entry count exceeds 5000 at the request layer.
- Pre-validate archives before importing.
When it happens
Trigger: Uploading a Claude Design zip archive that declares 5000 or more file entries. A zip bomb or a maliciously crafted archive intended to exhaust file handles or disk.
Common situations: A legitimate but very large export (rare for Claude Design HTML exports). A malicious archive. An archive that includes `node_modules` or other bulky directory trees by accident.
Related errors
- zip file too large: ${relPath}
- zip is too large
- zip entry size mismatch: ${relPath}
- zip contains no files
- zip does not contain an HTML file
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/681369a7a512d33b.
Report an issue: GitHub.