{"record":{"id":"cd3c956a4b7f9fdb","repo":"nexu-io/open-design","slug":"zip-is-too-large","errorCode":null,"errorMessage":"zip is too large","messagePattern":"zip is too large","errorType":"validation","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/daemon/src/design/claude-design-import.ts","lineNumber":51,"sourceCode":"    if (files.length >= MAX_FILES) throw new Error('zip contains too many files');\n    const relPath = sanitizeZipPath(entry.name);\n    if (entry.uncompressedSize > MAX_FILE_BYTES) {\n      throw new Error(`zip file too large: ${relPath}`);\n    }\n\n    // Decode first; the central directory's uncompressedSize is unreliable for\n    // streaming/data-descriptor zips (it can read 0 even when the payload\n    // carries real data). The inflate cap and the post-decode size checks below\n    // are authoritative.\n    const body = readEntryBody(zip, entry);\n    if (body.length > MAX_FILE_BYTES) {\n      throw new Error(`zip file too large: ${relPath}`);\n    }\n    if (entry.uncompressedSize > 0 && body.length !== entry.uncompressedSize) {\n      throw new Error(`zip entry size mismatch: ${relPath}`);\n    }\n    totalBytes += body.length;\n    if (totalBytes > MAX_TOTAL_BYTES) throw new Error('zip is too large');\n\n    files.push({ path: relPath, body: normalizeImportedClaudeDesignFile(relPath, body) });\n  }\n\n  if (files.length === 0) throw new Error('zip contains no files');\n  const entryFile = chooseEntryFile(files.map((f) => f.path));\n  if (!entryFile) throw new Error('zip does not contain an HTML file');\n\n  const dirCreates = new Map<string, Promise<string | undefined>>();\n  const ensureDir = (dir: string) => {\n    let pending = dirCreates.get(dir);\n    if (!pending) {\n      pending = mkdir(dir, { recursive: true });\n      dirCreates.set(dir, pending);\n    }\n    return pending;\n  };\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/design/claude-design-import.ts#L33-L69","documentation":"Thrown by `importClaudeDesignZip` when the running cumulative total of decoded entry body lengths exceeds `MAX_TOTAL_BYTES` (100 MiB). This is the aggregate zip-bomb guard, evaluated after each entry is decoded and added to the running total.","triggerScenarios":"An archive whose combined decoded entries exceed 100 MiB — e.g. many medium-sized files, or a few files that individually pass the 25 MiB per-file cap but together exceed the total.","commonSituations":"A legitimate but large export bundling many assets. An archive that includes bulky directories (`node_modules`, `dist`, media libraries). A zip bomb designed to pass per-file checks but blow the aggregate budget.","solutions":["Trim the archive contents so the total decoded size is at or below 100 MiB.","Exclude bulky directories and externalize large media.","Re-export selecting only the HTML and essential assets."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const MAX_TOTAL_BYTES = 100 * 1024 * 1024;\nlet total = 0;\nfor (const entry of entries.filter((e) => !e.isDirectory)) {\n  total += readEntryBody(zip, entry).length;\n  if (total > MAX_TOTAL_BYTES) {\n    throw new Error(`archive total ${total} exceeds 100 MiB cap`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await importClaudeDesignZip(zipPath, projectDir);\n} catch (err) {\n  if (err instanceof Error && err.message === 'zip is too large') {\n    // trim aggregate archive size below 100 MiB and re-upload\n  }\n  throw err;\n}","preventionTips":["Keep total archive content under 100 MiB.","Exclude bulky directories and externalize large media before zipping.","Pre-scan aggregate decoded size before importing."],"tags":["zip","claude-design","size-limit","import","zip-bomb"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}