actualbudget/actual · error · UnsafeZipError

Zip entry "${file.name}" exceeds maximum size of ${maxEntryS

Error message

Zip entry "${file.name}" exceeds maximum size of ${maxEntrySize} bytes

What it means

During unzipSync's filter callback, each entry's uncompressed size (originalSize) is checked against maxEntrySize before extraction. Any single entry exceeding the limit throws UnsafeZipError with zipReason 'entry-size', naming the entry and the limit. This prevents one huge file inside the archive from exhausting memory.

Source

Thrown at packages/loot-core/src/server/util/zip.ts:67

  }: SafeUnzipOptions = {},
): Unzipped {
  if (data.length > maxArchiveSize) {
    throw new UnsafeZipError(
      `Zip archive exceeds maximum size of ${maxArchiveSize} bytes`,
      { zipReason: 'archive-size', maxSize: maxArchiveSize },
    );
  }

  const seen = new Set<string>();

  let totalUncompressedSize = 0;

  return unzipSync(data, {
    filter(file) {
      assertSafeEntryName(file.name);

      if (file.originalSize > maxEntrySize) {
        throw new UnsafeZipError(
          `Zip entry "${file.name}" exceeds maximum size of ${maxEntrySize} bytes`,
          {
            zipReason: 'entry-size',
            entryName: file.name,
            maxSize: maxEntrySize,
          },
        );
      }

      totalUncompressedSize += file.originalSize;
      if (totalUncompressedSize > maxTotalUncompressedSize) {
        throw new UnsafeZipError(
          `Zip archive's total uncompressed size exceeds maximum of ${maxTotalUncompressedSize} bytes`,
          { zipReason: 'total-size', maxSize: maxTotalUncompressedSize },
        );
      }

      const normalized = file.name.toLowerCase();

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Inspect the named entry — if it is unexpected, treat the archive as malicious and reject it.
  2. For legitimate large entries, raise maxEntrySize when calling safeUnzip.
  3. Check entry sizes from the central directory before extraction in your own pre-scan and report a friendly error.
  4. Never bypass the check on untrusted archives; process oversized entries in a worker with a hard memory cap instead.

Example fix

// before
const entries = safeUnzip(buffer);
// after
const entries = safeUnzip(buffer, {
  maxEntrySize: 200 * 1024 * 1024, // allow bigger individual files
});
Defensive patterns

Strategy: try-catch

Validate before calling

// Read central directory sizes before extraction if available, or cap input:
if (buf.length > MAX_ARCHIVE) throw new Error('Archive too large to inspect safely');

Try / catch

try {
  const entries = safeUnzip(buf);
} catch (e) {
  if (e instanceof UnsafeZipError && e.zipReason === 'entry-size') {
    showError(`Entry "${e.entryName}" is too large (limit ${e.maxSize} bytes)`);
  } else { throw e; }
}

Prevention

When it happens

Trigger: A zip containing one entry whose decompressed size exceeds maxEntrySize (default MAX_ZIP_SIZE) — a decompression bomb with a small compressed payload expanding to gigabytes, or a legitimately huge single export file.

Common situations: Malicious archives designed to expand massively on extraction; backups containing very large embedded attachments; importing third-party archives of unknown content.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/ed086ee3b0bc4e12. Report an issue: GitHub.