actualbudget/actual · error · UnsafeZipError

Zip archive exceeds maximum size of ${maxArchiveSize} bytes

Error message

Zip archive exceeds maximum size of ${maxArchiveSize} bytes

What it means

safeUnzip enforces a maximum compressed archive size before decompressing to prevent decompression-bomb / resource-exhaustion attacks. If the raw buffer exceeds maxArchiveSize (default MAX_ZIP_SIZE), it throws UnsafeZipError with zipReason 'archive-size' and the configured limit.

Source

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

  }
}

type SafeUnzipOptions = {
  maxArchiveSize?: number;
  maxEntrySize?: number;
  maxTotalUncompressedSize?: number;
};

export function safeUnzip(
  data: Uint8Array,
  {
    maxArchiveSize = MAX_ZIP_SIZE,
    maxEntrySize = MAX_ZIP_SIZE,
    maxTotalUncompressedSize = MAX_ZIP_SIZE,
  }: 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',

View on GitHub (pinned to d4334cb6e6)

Solutions

  1. Check the file size client-side before upload/import and reject archives over the limit early with a clear message.
  2. If legitimate larger backups are expected, pass a higher maxArchiveSize option to safeUnzip.
  3. Enforce an HTTP request body limit at the server/proxy layer so oversized uploads fail fast.
  4. Split or prune the backup (remove attachments) to reduce archive size.

Example fix

// before
const entries = safeUnzip(buffer); // default 25MB-ish limit
// after
const MAX = 100 * 1024 * 1024;
if (buffer.length > MAX) {
  throw new Error(`Backup too large (${buffer.length} bytes); limit is ${MAX}`);
}
const entries = safeUnzip(buffer, { maxArchiveSize: MAX });
Defensive patterns

Strategy: validation

Validate before calling

const MAX_ARCHIVE = 25 * 1024 * 1024;
if (data.length > MAX_ARCHIVE) {
  throw new Error(`Archive is ${(data.length / 1e6).toFixed(1)}MB; limit is ${MAX_ARCHIVE / 1e6}MB`);
}

Try / catch

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

Prevention

When it happens

Trigger: Importing or loading a backup zip larger than the configured limit — a legitimately huge budget backup, or an intentionally oversized malicious upload. Callers include loadBackup, importBuffer, and parseFile.

Common situations: Users importing very large backup archives containing many attachments; a server receiving oversized uploads without an HTTP-level body size limit; misconfigured smaller maxArchiveSize rejecting valid archives.

Related errors


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