chatboxai/chatbox · error

ZIP archive is missing its central directory

Error message

ZIP archive is missing its central directory

What it means

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.

Source

Thrown at src/renderer/packages/backup/zip.ts:188

async function validateZipEndOfCentralDirectory(file: File) {
  const minimumRecordSize = 22
  const maximumCommentSize = 65_535
  if (file.size < minimumRecordSize) throw new Error('ZIP archive is truncated')
  const tailSize = Math.min(file.size, minimumRecordSize + maximumCommentSize)
  const tail = new Uint8Array(await file.slice(file.size - tailSize).arrayBuffer())
  let recordOffset = -1
  for (let index = tail.length - minimumRecordSize; index >= 0; index--) {
    if (tail[index] === 0x50 && tail[index + 1] === 0x4b && tail[index + 2] === 0x05 && tail[index + 3] === 0x06) {
      const candidateView = new DataView(tail.buffer, tail.byteOffset + index, tail.length - index)
      const commentLength = candidateView.getUint16(20, true)
      if (index + minimumRecordSize + commentLength === tail.length) {
        recordOffset = index
        break
      }
    }
  }
  if (recordOffset < 0) throw new Error('ZIP archive is missing its central directory')
  const view = new DataView(tail.buffer, tail.byteOffset + recordOffset, tail.length - recordOffset)
  const centralDirectorySize = view.getUint32(12, true)
  const centralDirectoryOffset = view.getUint32(16, true)
  if (centralDirectoryOffset + centralDirectorySize > file.size - (tail.length - recordOffset)) {
    throw new Error('ZIP archive central directory is invalid')
  }
}

function combineChunks(chunks: Uint8Array[], totalSize: number): Uint8Array {
  const output = new Uint8Array(totalSize)
  let offset = 0
  for (const chunk of chunks) {
    output.set(chunk, offset)
    offset += chunk.length
  }
  return output
}

View on GitHub (pinned to 81571269ad)

Solutions

  1. Verify the file is actually a ZIP (magic bytes 'PK' at offset 0) before importing.
  2. Re-download or re-export the backup.
  3. If repairing, use a zip-repair tool to rebuild the central directory.

Example fix

// before
await readZipFileEntries(file, onEntry, options)

// after
const head = new Uint8Array(await file.slice(0, 4).arrayBuffer())
const isZip = head[0] === 0x50 && head[1] === 0x4b
if (!isZip) throw new Error('Selected file is not a ZIP archive')
await readZipFileEntries(file, onEntry, options)
Defensive patterns

Strategy: try-catch

Validate before calling

async function hasZipMagic(file: File): Promise<boolean> {
  if (file.size < 4) return false
  const head = new Uint8Array(await file.slice(0, 4).arrayBuffer())
  return head[0] === 0x50 && head[1] === 0x4b
}

Try / catch

try {
  await readZipFileEntries(file, onEntry, options)
} catch (error) {
  if (/missing its central directory/.test((error as Error).message)) notifyUser('Backup archive is corrupt')
  throw error
}

Prevention

When it happens

Trigger: 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.

Common situations: User picks a non-ZIP file; archive corrupted in transit; archive truncated so the EOCD is missing; the comment-length field is corrupted.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/e4bd2c03e3c0e81b. Report an issue: GitHub.