chatboxai/chatbox · error · Error

Backup contains too many entries

Error message

Backup contains too many entries

What it means

Thrown by enforceArchiveLimits() when the number of zip entries exceeds DEFAULT_ZIP_LIMITS.maxEntries (100,004). This is the zip-bomb / unbounded-growth guard at the archive level — it counts every entry (JSON and resource) and aborts before the zip writer consumes excessive file handles or metadata. The check fires on the entry-count branch before per-entry size is evaluated.

Source

Thrown at src/renderer/packages/backup/export-backup.ts:112

  path: string,
  value: unknown
): Promise<{ archive: ZipArchiveEntry; descriptor: BackupJsonEntry }> {
  const bytes = new TextEncoder().encode(JSON.stringify(value))
  if (bytes.length > MAX_BACKUP_JSON_ENTRY_BYTES) throw new Error(`Backup JSON entry is too large: ${path}`)
  return {
    archive: { path, data: bytes, compress: true },
    descriptor: { path, size: bytes.length, checksum: await sha256Checksum(bytes) },
  }
}

async function* enforceArchiveLimits(
  entries: AsyncIterable<ZipArchiveEntry>
): AsyncGenerator<ZipArchiveEntry, void, unknown> {
  let entryCount = 0
  let totalUncompressedBytes = 0
  for await (const entry of entries) {
    entryCount++
    if (entryCount > DEFAULT_ZIP_LIMITS.maxEntries) throw new Error('Backup contains too many entries')
    const maxEntryBytes = backupEntryByteLimit(entry.path)
    if (entry.data instanceof Uint8Array) {
      if (entry.data.length > maxEntryBytes) throw new Error(`Backup entry is too large: ${entry.path}`)
      totalUncompressedBytes += entry.data.length
      if (totalUncompressedBytes > DEFAULT_ZIP_LIMITS.maxTotalUncompressedBytes) {
        throw new Error('Backup uncompressed size exceeds the safety limit')
      }
      yield entry
      continue
    }
    let entryBytes = 0
    const data = entry.data
    yield {
      ...entry,
      data: (async function* () {
        for await (const chunk of data) {
          entryBytes += chunk.length
          totalUncompressedBytes += chunk.length

View on GitHub (pinned to 81571269ad)

Solutions

  1. Export a subset of conversations rather than the entire history.
  2. Deduplicate resources (the backup already has resource-candidate logic — verify it is applied) so identical files are stored once.
  3. Archive older conversations into separate backup files so each stays under the entry cap.
Defensive patterns

Strategy: validation

Validate before calling

const MAX_ENTRIES = 100_004
function estimateEntryCount(sessions: unknown[], resourcesPerSession: number): number {
  return sessions.length + sessions.length * resourcesPerSession
}
if (estimateEntryCount(sessions, avgResources) > MAX_ENTRIES) {
  throw new Error('Too many entries for a single backup; narrow the scope')
}

Try / catch

try {
  await exportBackup({ items: ['conversations'] })
} catch (error) {
  if (error instanceof Error && /too many entries/i.test(error.message)) {
    showToast('Backup is too large. Export fewer conversations.')
  } else throw error
}

Prevention

When it happens

Trigger: A backup export produces more than 100,004 archive members. Each session contributes one JSON entry plus N resource entries (images, files), so a very large session count or a session with many attachments can exceed the cap. The check runs as entries stream through enforceArchiveLimits.

Common situations: User has tens of thousands of conversations with attachments; a corrupted store produced duplicate entries; resources were not deduplicated so each message embeds its own copy. The 100,004 ceiling matches the zip format's 16-bit entry limit headroom (65535 for classic zip) with a safety margin for the writer used.

Related errors


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