can1357/oh-my-pi · error · ArchiveError

Invalid ZIP archive: local and central compression methods d

Error message

Invalid ZIP archive: local and central compression methods disagree for '${memberPath}'

What it means

Thrown when the compression method recorded in the member's local file header (offset 8) differs from the method in its central directory entry. A well-formed ZIP must have identical methods in both headers; disagreement means the archive was corrupted or hand-modified, so the library refuses to decode with either guess. ArchiveError naming the member.

Source

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

			if (SUPPORTED_METHODS[this.#method] !== true) {
				throw new ArchiveError(`Unsupported ZIP compression method ${this.#method} for '${memberPath}'`);
			}
			const headerEnd = checkedEnd(
				this.#localHeaderOffset,
				30,
				this.#source.size,
				`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})`,

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the archive (`unzip -t`); if it fails, the file is corrupt — re-obtain it
  2. Re-create the archive from the original source files rather than repairing
  3. If repairing is required, use `zip -FF` which keeps headers consistent
  4. If the archive comes from your own writer, fix the writer to emit matching local/central methods

Example fix

// before: reading a hex-edited zip
await readZipMember(zip, 'data.bin'); // throws 3713
// after: repair then read
// $ `zip -FF broken.zip --out fixed.zip`
await readZipMember(await readZip(Bun.file('fixed.zip')), 'data.bin');
Defensive patterns

Strategy: validation

Validate before calling

// Cross-check local vs central method before reading
if (localHeaderMethod(entry) !== entry.method) throw new Error('zip headers inconsistent — archive is corrupt');

Try / catch

try {
  const data = await zip.read(member);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('methods disagree')) {
    // attempt structural repair once: await $`zip -FF broken.zip --out fixed.zip`
  } else throw err;
}

Prevention

When it happens

Trigger: ZipMemberSource read where central directory says method X but the local header at localHeaderOffset says method Y — typically after byte-level patching, corrupting writes, or maliciously crafted archives.

Common situations: Archives repaired by naive tools that rewrite one header but not the other; files edited in hex or by scripts patching sizes/methods; deliberately malformed zips (fuzzing/security research); media with bit-rot.

Related errors


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