can1357/oh-my-pi · error · ArchiveError

ZIP archive is too large to write

Error message

ZIP archive is too large to write

What it means

Final sanity check in the ZIP writer: the accumulated local-data size plus central-directory size must stay a safe integer, and the member count must not exceed U32_MAX. Exceeding these means the archive bookkeeping can no longer be represented correctly, so the writer aborts.

Source

Thrown at packages/utils/src/ar/zip.ts:674

			writeUInt16LE(central, 4, (UNIX_HOST << 8) | 20);
			writeUInt16LE(central, 6, 20);
			writeUInt16LE(central, 8, UTF8_FLAG);
			writeUInt16LE(central, 10, method);
			writeUInt16LE(central, 12, 0);
			writeUInt16LE(central, 14, 0x21);
			writeUInt32LE(central, 16, checksum);
			writeUInt32LE(central, 20, payload.byteLength);
			writeUInt32LE(central, 24, data.byteLength);
			writeUInt16LE(central, 28, nameBytes.byteLength);
			writeUInt32LE(central, 38, (0o100644 << 16) >>> 0);
			writeUInt32LE(central, 42, localSize);
			central.set(nameBytes, 46);
			centralParts.push(central);
			localSize += local.byteLength + payload.byteLength;
			centralSize += central.byteLength;
			count++;
			if (!Number.isSafeInteger(localSize + centralSize) || count > U32_MAX) {
				throw new ArchiveError("ZIP archive is too large to write");
			}
		}
		if (localSize >= U32_MAX || centralSize >= U32_MAX) {
			throw new ArchiveError("ZIP archive is too large to write member offsets safely");
		}
		const zip64 = count >= U16_MAX;
		const trailerLength = EOCD_LENGTH + (zip64 ? ZIP64_EOCD_LENGTH + ZIP64_LOCATOR_LENGTH : 0);
		const totalSize = localSize + centralSize + trailerLength;
		if (!Number.isSafeInteger(totalSize)) throw new ArchiveError("ZIP archive is too large to write");
		const output = new Uint8Array(totalSize);
		let outputOffset = 0;
		for (const part of localParts) {
			output.set(part, outputOffset);
			outputOffset += part.byteLength;
		}
		for (const part of centralParts) {
			output.set(part, outputOffset);
			outputOffset += part.byteLength;

View on GitHub (pinned to 9690622007)

Solutions

  1. Split the members across multiple ZIP archives (batch writes, e.g. 10k-100k members per archive)
  2. Deduplicate or exclude generated files before archiving (respect ignore lists)
  3. Check member count and total size before calling the writer and shard accordingly
  4. Use a streaming archiver designed for very large entry counts

Example fix

// before: single zip of every file
await writeZip(millionsOfMembers);
// after: batch
for (let i = 0; i < millionsOfMembers.length; i += 50000)
  await writeZip(millionsOfMembers.slice(i, i + 50000).map((m, j) => ({ ...m, name: `part${i / 50000}/${m.name}` })));
Defensive patterns

Strategy: validation

Validate before calling

function assertZipBookkeepingSafe(members: { name: string; data: Uint8Array }[]): void {
  const count = members.length;
  const total = members.reduce((s, m) => s + 30 + Buffer.byteLength(m.name, 'utf8') + m.data.byteLength, 0) + count * 46;
  if (!Number.isSafeInteger(total) || count > 0xffffffff)
    throw new Error(`zip too large: ${count} entries, ${total} bytes`);
}

Try / catch

try {
  await writeZip(members);
} catch (err) {
  if (err instanceof ArchiveError && err.message === 'ZIP archive is too large to write') {
    logger.error('zip bookkeeping overflow', { entryCount: members.length });
    return batchWriteZips(members, 50000);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the zip write API with an enormous number of members (> 4,294,967,295 entries) or sizes whose running total overflows Number.isSafeInteger (>= 2^53 bytes) — practically, extremely large in-memory archive builds.

Common situations: Archiving millions of tiny files (e.g. node_modules, extracted datasets) into one zip in memory; runaway code adding members in a loop without a bound; accumulator bugs in generated member lists.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/a105af9cdf4cbfa3. Report an issue: GitHub.