can1357/oh-my-pi · error · ArchiveError

Invalid ZIP archive: size mismatch for '${memberPath}' (expe

Error message

Invalid ZIP archive: size mismatch for '${memberPath}' (expected ${size}, got ${this.#compressedSize})

What it means

Thrown for stored (method 0) members whose central-directory uncompressed size differs from the compressed size. Stored data must be byte-identical to the original, so both size fields must match; a mismatch means the header metadata is wrong or the data was altered. ArchiveError including expected vs actual sizes.

Source

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

				`local header for '${memberPath}'`,
			);
			const header = await this.#source.read(this.#localHeaderOffset, headerEnd);
			if (header.byteLength !== 30 || readUInt32LE(header, 0) !== LOCAL_HEADER_SIGNATURE) {
				throw new ArchiveError(`Invalid ZIP archive: malformed local header for '${memberPath}'`);
			}
			const localFlags = readUInt16LE(header, 6);
			if ((localFlags & (ENCRYPTED_FLAG | STRONG_ENCRYPTION_FLAG)) !== 0) {
				throw new ArchiveError(`Encrypted ZIP member '${memberPath}' is not supported`);
			}
			if (readUInt16LE(header, 8) !== this.#method) {
				throw new ArchiveError(
					`Invalid ZIP archive: local and central compression methods disagree for '${memberPath}'`,
				);
			}
			const dataStart = this.#localHeaderOffset + 30 + readUInt16LE(header, 26) + readUInt16LE(header, 28);
			const dataEnd = checkedEnd(dataStart, this.#compressedSize, this.#source.size, `data for '${memberPath}'`);
			if (this.#method === 0 && this.#compressedSize !== size) {
				throw new ArchiveError(
					`Invalid ZIP archive: size mismatch for '${memberPath}' (expected ${size}, got ${this.#compressedSize})`,
				);
			}
			const compressed = await this.#source.read(dataStart, dataEnd);
			if (compressed.byteLength !== this.#compressedSize) {
				throw new ArchiveError(`Invalid ZIP archive: truncated data for '${memberPath}'`);
			}
			const decoded = await decodeMember(compressed, this.#method, size, memberPath);
			if (decoded.byteLength !== size) {
				throw new ArchiveError(
					`Invalid ZIP archive: size mismatch for '${memberPath}' (expected ${size}, got ${decoded.byteLength})`,
				);
			}
			const actualCrc = crc32(decoded);
			if (actualCrc !== this.#crc) {
				throw new ArchiveError(`Invalid ZIP archive: CRC mismatch for '${memberPath}'`);
			}
			return decoded;

View on GitHub (pinned to 9690622007)

Solutions

  1. Validate with `unzip -t` and re-obtain a clean copy of the archive
  2. Re-zip the source files to regenerate correct size fields
  3. If you wrote the archive, fix the writer to update both compressed and uncompressed sizes for stored entries
  4. For streamed writing, use data descriptors correctly or seek back to patch the local header

Example fix

// before: writer stores size only in central directory
await readZipMember(zip, 'blob.raw'); // throws 3714
// after: re-create with consistent headers
// $ `zip -0 -r fixed.zip dir/`
await readZipMember(await readZip(Bun.file('fixed.zip')), 'blob.raw');
Defensive patterns

Strategy: validation

Validate before calling

// For stored entries, compressed and uncompressed sizes must match
if (entry.method === 0 && entry.compressedSize !== entry.size) {
  throw new Error('stored entry has inconsistent sizes — regenerate archive');
}

Try / catch

try {
  const data = await zip.read(member);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('size mismatch')) {
    throw new Error('ZIP metadata inconsistent for stored member; re-zip the source data');
  }
  throw err;
}

Prevention

When it happens

Trigger: Reading a method-0 member where #compressedSize !== size — e.g. sizes patched inconsistently in the two header sets, or data appended/replaced without updating both size fields; note data-descriptor (streamed) zips that store 0 sizes legitimately take a different path per spec, but corrupted ones hit this check.

Common situations: Zips produced by broken streaming writers that mis-report stored sizes; files whose data was edited in place; archives damaged in transit; maliciously crafted zips.

Related errors


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