chatboxai/chatbox · error
ZIP archive is truncated
Error message
ZIP archive is truncated
What it means
Thrown by validateZipEndOfCentralDirectory when the input file is smaller than 22 bytes — the minimum size of a ZIP End Of Central Directory record. A file that small cannot be a valid ZIP, so it is rejected before any parsing begins.
Source
Thrown at src/renderer/packages/backup/zip.ts:174
}
} finally {
signal?.removeEventListener('abort', onAbort)
zip.terminate()
notifyProducer(true)
}
}
export interface ReadZipEntry {
path: string
data: Uint8Array
compressedSize?: number
uncompressedSize: number
}
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)) {View on GitHub (pinned to 81571269ad)
Solutions
- Check file.size >= 22 (and realistically >= a few hundred bytes) before calling readZipFileEntries.
- Re-download or re-export the backup.
- Show a file-size/type guard in the import UI.
Example fix
// before
await readZipFileEntries(file, onEntry, options)
// after
const MIN_ZIP = 22
if (file.size < MIN_ZIP) throw new Error('File is too small to be a ZIP archive')
await readZipFileEntries(file, onEntry, options) Defensive patterns
Strategy: validation
Validate before calling
const MIN_ZIP_EOCD_BYTES = 22
function isLargeEnoughToBeZip(file: File): boolean {
return file.size >= MIN_ZIP_EOCD_BYTES
} Try / catch
try {
await readZipFileEntries(file, onEntry, options)
} catch (error) {
if (/truncated/.test((error as Error).message)) notifyUser('Backup file is incomplete')
throw error
} Prevention
- Show a minimum-size guard in the import UI.
- Confirm downloads completed (compare size to Content-Length) before importing.
- Reject 0-byte files at the file picker.
When it happens
Trigger: readZipFileEntries is called with a File whose size < 22 (e.g. an empty file, a 0-byte stub, or a tiny non-zip blob).
Common situations: User selects an empty or incompletely-downloaded file; a truncated upload; the wrong file type picked in the dialog.
Related errors
- Unsafe ZIP entry path: ${path}
- Duplicate ZIP entry: ${entry.path}
- ZIP archive is missing its central directory
- ZIP archive central directory is invalid
- local_parser_file_too_large
AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12).
Data as JSON: /api/errors/aae8f61661fcc0b8.
Report an issue: GitHub.