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

  1. Reduce the archive to fewer than 5000 file entries by removing unnecessary files (e.g. `node_modules`, build output).
  2. If exporting from a tool, configure it to emit only the HTML and required assets.
  3. 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

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


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/681369a7a512d33b. Report an issue: GitHub.