{"record":{"id":"574688f331a3f007","repo":"nexu-io/open-design","slug":"zip-contains-no-files","errorCode":null,"errorMessage":"zip contains no files","messagePattern":"zip contains no files","errorType":"validation","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/daemon/src/design/claude-design-import.ts","lineNumber":56,"sourceCode":"\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\n  await mkdir(projectDir, { recursive: true });\n  await Promise.all(files.map(async (f) => {\n    const target = safeJoin(projectDir, f.path);\n    await ensureDir(path.dirname(target));\n    await writeFile(target, f.body);","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/design/claude-design-import.ts#L38-L74","documentation":"Thrown by `importClaudeDesignZip` after the entry loop completes if no decodable file entries were collected (`files.length === 0`). This happens when the archive contains only directory entries, or has no entries at all. There is nothing to import, so the importer fails fast rather than producing an empty project.","triggerScenarios":"Uploading an empty zip. Uploading a zip that contains only directory entries (all entries end with `/`). An archive whose entries were all filtered out as directories.","commonSituations":"An empty or placeholder archive. A zip created by accidentally zipping a folder's structure without its files. A tool that emitted directory entries but skipped file payloads.","solutions":["Re-create the archive ensuring it contains at least one real file entry.","Verify the source export actually produced files; re-export if necessary.","Check that the zip tool did not store files as directory-only entries."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const fileEntries = entries.filter((e) => !e.isDirectory);\nif (fileEntries.length === 0) {\n  throw new Error('archive contains no file entries');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await importClaudeDesignZip(zipPath, projectDir);\n} catch (err) {\n  if (err instanceof Error && err.message === 'zip contains no files') {\n    // re-create the archive with at least one real file\n  }\n  throw err;\n}","preventionTips":["Ensure the source export actually contains files before zipping.","Verify the archive includes file entries (not only directories) before uploading.","Reject empty archives at the request layer."],"tags":["zip","claude-design","validation","import"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}