can1357/oh-my-pi · error · ArchiveError

Invalid ZIP archive: malformed LZMA properties for '${member

Error message

Invalid ZIP archive: malformed LZMA properties for '${memberPath}'

What it means

A ZIP member compressed with method 14 (LZMA) must begin with a 4-byte LZMA SDK header whose bytes 2 and 3 are the properties size marker 0x05 0x00, followed by 5 bytes of LZMA properties and the LZMA stream. This member's compressed data is shorter than 9 bytes or lacks the 0x05 0x00 marker, so it is not a valid LZMA-in-ZIP payload.

Source

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

}

async function decodeMember(
	compressed: Uint8Array,
	method: number,
	size: number,
	memberPath: string,
): Promise<Uint8Array> {
	try {
		switch (method) {
			case 0:
				return compressed;
			case 8:
				return zlib.inflateRawSync(compressed, { maxOutputLength: Math.max(size, 1) });
			case 12:
				return await bzip2Decompress(compressed, size);
			case 14: {
				if (compressed.byteLength < 9 || compressed[2] !== 5 || compressed[3] !== 0) {
					throw new ArchiveError(`Invalid ZIP archive: malformed LZMA properties for '${memberPath}'`);
				}
				return await lzmaDecompress(compressed.subarray(4, 9), compressed.subarray(9), size);
			}
			case 20:
			case 93:
				return await zstdDecompress(compressed, size);
			case 95:
				return await xzDecompress(compressed, size);
			default:
				throw new ArchiveError(`Unsupported ZIP compression method ${method} for '${memberPath}'`);
		}
	} catch (error) {
		throw archiveError(error, `Failed to decompress ZIP member '${memberPath}'`);
	}
}

class ZipMemberSource implements MemberSource {
	readonly #source: ByteSource;

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the archive with unzip -t / 7z t and re-obtain if corrupt.
  2. Repack the archive with 7-Zip (7z a -tzip -m0=lzma) so the header/properties bytes are written correctly.
  3. If you write LZMA entries yourself, prepend the 4-byte header (version 2 bytes, then 0x05 0x00) plus the 5-byte LZMA properties block before the stream, matching the ZIP APPNOTE method-14 layout.

Example fix

// before: raw LZMA stream stored directly in the entry
// after
// payload = concat(Uint8Array([0x02,0x00,0x05,0x00]), lzmaProperties5Bytes, lzmaStream);
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await $`unzip -lv archive.zip`.quiet().nothrow();
if (res.exitCode === 0 && (await res.text()).includes("LZMA")) {
  // method 14 present: ensure archive came from a conformant writer (e.g. 7-Zip)
}

Try / catch

try {
  await archive.readMember(path);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("malformed LZMA properties")) {
    // repack with 7z: 7z x corrupted.zip -o tmp && 7z a -tzip -m0=lzma fixed.zip tmp/*
  } else throw err;
}

Prevention

When it happens

Trigger: Reading a ZIP entry whose compression method is 14 whose stored data is truncated (fewer than 9 bytes) or whose bytes at offsets 2-3 are not 0x05,0x00 — i.e. not produced by a conformant LZMA ZIP writer.

Common situations: Corrupted or truncated downloads of LZMA-compressed archives, hand-rolled writers that store raw LZMA without the 5-byte ZIP LZMA properties header, malicious/fuzzed archives.

Understand the failure class

Related errors


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