{"record":{"id":"e4bd2c03e3c0e81b","repo":"chatboxai/chatbox","slug":"zip-archive-is-missing-its-central-directory","errorCode":null,"errorMessage":"ZIP archive is missing its central directory","messagePattern":"ZIP archive is missing its central directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/renderer/packages/backup/zip.ts","lineNumber":188,"sourceCode":"\nasync function validateZipEndOfCentralDirectory(file: File) {\n  const minimumRecordSize = 22\n  const maximumCommentSize = 65_535\n  if (file.size < minimumRecordSize) throw new Error('ZIP archive is truncated')\n  const tailSize = Math.min(file.size, minimumRecordSize + maximumCommentSize)\n  const tail = new Uint8Array(await file.slice(file.size - tailSize).arrayBuffer())\n  let recordOffset = -1\n  for (let index = tail.length - minimumRecordSize; index >= 0; index--) {\n    if (tail[index] === 0x50 && tail[index + 1] === 0x4b && tail[index + 2] === 0x05 && tail[index + 3] === 0x06) {\n      const candidateView = new DataView(tail.buffer, tail.byteOffset + index, tail.length - index)\n      const commentLength = candidateView.getUint16(20, true)\n      if (index + minimumRecordSize + commentLength === tail.length) {\n        recordOffset = index\n        break\n      }\n    }\n  }\n  if (recordOffset < 0) throw new Error('ZIP archive is missing its central directory')\n  const view = new DataView(tail.buffer, tail.byteOffset + recordOffset, tail.length - recordOffset)\n  const centralDirectorySize = view.getUint32(12, true)\n  const centralDirectoryOffset = view.getUint32(16, true)\n  if (centralDirectoryOffset + centralDirectorySize > file.size - (tail.length - recordOffset)) {\n    throw new Error('ZIP archive central directory is invalid')\n  }\n}\n\nfunction combineChunks(chunks: Uint8Array[], totalSize: number): Uint8Array {\n  const output = new Uint8Array(totalSize)\n  let offset = 0\n  for (const chunk of chunks) {\n    output.set(chunk, offset)\n    offset += chunk.length\n  }\n  return output\n}\n","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/packages/backup/zip.ts#L170-L206","documentation":"Thrown by validateZipEndOfCentralDirectory after scanning the file's tail for the EOCD signature (0x50 0x4b 0x05 0x06) and finding no candidate whose declared comment length lines up with the tail size. Without a valid EOCD the central directory cannot be located, so the archive is treated as corrupt or non-ZIP.","triggerScenarios":"No valid EOCD record found in the last (22 + 65535) bytes of the file — e.g. the file isn't a ZIP at all, or the EOCD was truncated or overwritten.","commonSituations":"User picks a non-ZIP file; archive corrupted in transit; archive truncated so the EOCD is missing; the comment-length field is corrupted.","solutions":["Verify the file is actually a ZIP (magic bytes 'PK' at offset 0) before importing.","Re-download or re-export the backup.","If repairing, use a zip-repair tool to rebuild the central directory."],"exampleFix":"// before\nawait readZipFileEntries(file, onEntry, options)\n\n// after\nconst head = new Uint8Array(await file.slice(0, 4).arrayBuffer())\nconst isZip = head[0] === 0x50 && head[1] === 0x4b\nif (!isZip) throw new Error('Selected file is not a ZIP archive')\nawait readZipFileEntries(file, onEntry, options)","handlingStrategy":"try-catch","validationCode":"async function hasZipMagic(file: File): Promise<boolean> {\n  if (file.size < 4) return false\n  const head = new Uint8Array(await file.slice(0, 4).arrayBuffer())\n  return head[0] === 0x50 && head[1] === 0x4b\n}","typeGuard":null,"tryCatchPattern":"try {\n  await readZipFileEntries(file, onEntry, options)\n} catch (error) {\n  if (/missing its central directory/.test((error as Error).message)) notifyUser('Backup archive is corrupt')\n  throw error\n}","preventionTips":["Check the 'PK' magic bytes at offset 0 before invoking the reader.","Re-download or re-export when an archive fails this check; do not retry the same bytes.","Verify archive integrity (size/transfer checksum) before import."],"tags":["zip","validation","corrupt-archive"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}