chatboxai/chatbox · error

ZIP archive central directory is invalid

Error message

ZIP archive central directory is invalid

What it means

Thrown by validateZipEndOfCentralDirectory after it locates the End-of-Central-Directory (EOCD) signature. The EOCD record advertises a central directory byte offset (getUint32 at 16) and size (getUint32 at 12); if their sum exceeds the file's remaining content space (file.size minus the tail region holding the EOCD), the archive's structural metadata is internally inconsistent and the file is rejected before any entry is read.

Source

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

  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
}

export async function readZipFileEntries(
  file: File,
  onEntry: (entry: ReadZipEntry) => Promise<void> | void,
  options: ZipReadOptions = {}
): Promise<void> {

View on GitHub (pinned to 81571269ad)

Solutions

  1. Re-export or re-download the backup file and retry; truncation/corruption is the dominant cause.
  2. Verify integrity against the source (checksum/size) before calling readZipFileEntries.
  3. Confirm the file is genuinely a zip (PK\x03\x04 local-file header at offset 0), not a sibling format that embedded a zip.
  4. If you control the producer, rewrite it so no bytes are appended after the EOCD.

Example fix

// before: trusting an arbitrary blob
await readZipFileEntries(file, onEntry)

// after: pre-flight structural sanity
const head = new Uint8Array(await file.slice(0, 4).arrayBuffer())
if (file.size < 22 || head[0] !== 0x50 || head[1] !== 0x4b) {
  throw new Error('Not a valid ZIP file')
}
await readZipFileEntries(file, onEntry)
Defensive patterns

Strategy: try-catch

Validate before calling

// Best-effort pre-flight: real signature + minimum size.
async function looksLikeValidZip(file: File): Promise<boolean> {
  if (file.size < 22) return false
  const head = new Uint8Array(await file.slice(0, 4).arrayBuffer())
  return head[0] === 0x50 && head[1] === 0x4b && head[2] === 0x03 && head[3] === 0x04
}

if (!(await looksLikeValidZip(file))) {
  throw new Error('File is not a valid ZIP archive')
}

Try / catch

try {
  await readZipFileEntries(file, onEntry, { signal })
} catch (error) {
  if (error instanceof Error && /central directory is invalid|missing its central directory|truncated/.test(error.message)) {
    // structural corruption: do not retry the same bytes; ask for a fresh export
    reportToUser('The backup file is corrupted. Please re-export and try again.')
  } else {
    throw error
  }
}

Prevention

When it happens

Trigger: Reproduces when a .zip is truncated mid-central-directory, when bytes are appended or stripped after creation (e.g. an outer container's footer shifts offsets), when a download finished early, or when the file is not actually a zip but happens to contain an EOCD signature.

Common situations: Restoring a backup that was only partially uploaded/downloaded, importing a file exported by a different tool that wrapped the zip, disk-full writes that cut the file short, or manual binary patching that invalidated offsets.

Related errors


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