chatboxai/chatbox · error

ZIP archive compression ratio is unsafe

Error message

ZIP archive compression ratio is unsafe

What it means

Whole-archive zip-bomb guard evaluated after all entries have streamed: thrown when totalUncompressedBytes exceeds 1 MiB AND exceeds Math.max(1, file.size) * limits.maxCompressionRatio (default 2000x). Unlike the per-entry check, this uses the cumulative decompressed byte count tracked across every entry's ondata chunks.

Solutions

  1. Raise limits.maxCompressionRatio for trusted backups known to compress well.
  2. Reject the archive for untrusted sources; archive-level >2000x compression is rarely benign.
  3. Re-export excluding redundant/highly-compressible payloads.

Example fix

// before
await readZipFileEntries(file, onEntry)

// after: raise the archive-level ratio cap for a trusted source
await readZipFileEntries(file, onEntry, {
  limits: { maxCompressionRatio: 5_000 }
})
Defensive patterns

Strategy: validation

Validate before calling

// Raise the archive-level ratio cap for a trusted, highly-compressible source.
await readZipFileEntries(file, onEntry, {
  limits: { maxCompressionRatio: 5_000 },
})

Try / catch

try {
  await readZipFileEntries(file, onEntry, opts)
} catch (error) {
  if (error instanceof Error && error.message === 'ZIP archive compression ratio is unsafe') {
    reportToUser('This archive is likely a zip-bomb and was rejected.')
  } else throw error
}

Prevention

When it happens

Trigger: An archive whose aggregate decompressed size is >2000x its on-disk size, achievable via many moderate-ratio entries that each pass the per-entry check, or a single entry that narrowly skirted it.

Common situations: Multi-entry zip-bombs, archives of highly compressible generated data, or backups of sparse virtual-disk images.

Related errors


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

Appendix: source

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

  try {
    while (true) {
      throwIfAborted(options.signal)
      const { done, value } = await reader.read()
      if (done) {
        unzip.push(EMPTY_BYTES, true)
        break
      }
      unzip.push(value)
      if (fatalError) throw fatalError
      if (pendingHandlers.length > 0) await Promise.all(pendingHandlers.splice(0))
    }
    if (fatalError) throw fatalError
    if (pendingHandlers.length > 0) await Promise.all(pendingHandlers.splice(0))
    if (
      totalUncompressedBytes > 1024 * 1024 &&
      totalUncompressedBytes > Math.max(1, file.size) * limits.maxCompressionRatio
    ) {
      throw new Error('ZIP archive compression ratio is unsafe')
    }
  } finally {
    await reader.cancel().catch(() => undefined)
  }
}

View on GitHub (pinned to 81571269ad)