{"record":{"id":"bbf9496136b06643","repo":"nexu-io/open-design","slug":"invalid-zip-central-directory-entry","errorCode":null,"errorMessage":"invalid zip central directory entry","messagePattern":"invalid zip central directory entry","errorType":"validation","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/daemon/src/design/claude-design-import.ts","lineNumber":196,"sourceCode":"    };\n    const onGestureEnd = (e) => { e.preventDefault(); e.stopPropagation(); isGesturing = false; };`);\n  return { result, wheelMatched, gestureMatched };\n}\n\nfunction readCentralDirectory(zip: Buffer): ZipEntry[] {\n  const eocdOffset = findEndOfCentralDirectory(zip);\n  const entryCount = zip.readUInt16LE(eocdOffset + 10);\n  const centralSize = zip.readUInt32LE(eocdOffset + 12);\n  const centralOffset = zip.readUInt32LE(eocdOffset + 16);\n  if (centralOffset + centralSize > zip.length) {\n    throw new Error('invalid zip central directory');\n  }\n\n  const entries: ZipEntry[] = [];\n  let offset = centralOffset;\n  for (let i = 0; i < entryCount; i += 1) {\n    if (zip.readUInt32LE(offset) !== CENTRAL_SIG) {\n      throw new Error('invalid zip central directory entry');\n    }\n    const flags = zip.readUInt16LE(offset + 8);\n    const method = zip.readUInt16LE(offset + 10);\n    const compressedSize = zip.readUInt32LE(offset + 20);\n    const uncompressedSize = zip.readUInt32LE(offset + 24);\n    const nameLen = zip.readUInt16LE(offset + 28);\n    const extraLen = zip.readUInt16LE(offset + 30);\n    const commentLen = zip.readUInt16LE(offset + 32);\n    const localOffset = zip.readUInt32LE(offset + 42);\n    const name = zip.slice(offset + 46, offset + 46 + nameLen).toString('utf8');\n    if ((flags & 1) !== 0) throw new Error('encrypted zip entries are not supported');\n    if (method !== 0 && method !== 8) {\n      throw new Error(`unsupported zip compression method: ${method}`);\n    }\n    entries.push({\n      name,\n      method,\n      compressedSize,","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/design/claude-design-import.ts#L178-L214","documentation":"Thrown by readCentralDirectory while walking the declared number of central-directory entries. Each entry must begin with the central-directory file-header signature 0x02014b50 (CENTRAL_SIG). If the 4 bytes at the current offset don't match, the central directory is corrupt, truncated, or the walking offset has desynchronized because a prior entry's nameLen/extraLen/commentLen fields were wrong. The HTTP route POST /api/import/claude-design maps this to 400.","triggerScenarios":"Calling importClaudeDesignZip (or uploading a multipart 'file' to POST /api/import/claude-design) on a zip whose central directory is malformed: a truncated archive, an entry whose declared name/extra/comment lengths push the next offset into invalid data, or a central directory that was copied from a different archive.","commonSituations":"A file renamed to .zip that isn't a real archive; a partially downloaded/uploaded zip; a zip produced by a non-conformant packager; corruption introduced by a proxy/CDN truncating the multipart upload; a zip edited after creation.","solutions":["Re-export the Claude Design project to a fresh zip and retry the upload.","Verify the archive opens in a standard unzip tool (e.g. `unzip -t file.zip`) before uploading.","Confirm the multipart upload completed fully (compare uploaded size to source size).","If the zip is generated programmatically, validate central-directory consistency with a conformant zip library before sending."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-flight the archive with a conformant unzip before importing.\nimport { execFileSync } from 'node:child_process';\nfunction zipIsSound(file: string): boolean {\n  try {\n    execFileSync('unzip', ['-tqq', file], { stdio: 'ignore' });\n    return true;\n  } catch {\n    return false;\n  }\n}\nif (!zipIsSound(uploadPath)) return res.status(400).json({ error: 'corrupt zip' });","typeGuard":null,"tryCatchPattern":"// importClaudeDesignZip throws a plain Error; catch at the route/caller.\ntry {\n  const imported = await importClaudeDesignZip(zipPath, projectDir);\n} catch (err) {\n  // All zip-structure errors surface here; the HTTP route returns 400.\n  return res.status(400).json({ error: String(err) });\n}","preventionTips":["Run `unzip -t` on every uploaded zip before calling importClaudeDesignZip.","Reject uploads whose declared size doesn't match the received size.","Regenerate zips only with standard tools (Info-ZIP, Finder, Windows Explorer)."],"tags":["zip","parsing","validation","import","claude-design"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}