can1357/oh-my-pi · error · ArchiveError

Invalid XZ stream: header position or magic is invalid

Error message

Invalid XZ stream: header position or magic is invalid

What it means

After parsing the footer and index, discoverStreams() computes where the stream header must begin (indexStart minus total block sizes minus the 12-byte header) and verifies the position is in-bounds and carries the 6-byte XZ magic (fd 37 7a 58 5a 00). If the computed position is negative, out of range, or the magic bytes are absent, the buffer is not a structurally valid XZ stream.

Source

Thrown at packages/utils/src/ar/codecs/xz.ts:140

			throw new ArchiveError("Invalid XZ stream: footer CRC32 mismatch");
		const flag0 = bytes[footerStart + 8]!;
		const flag1 = bytes[footerStart + 9]!;
		if (flag0 !== 0 || (flag1 & 0xf0) !== 0) throw new ArchiveError("Unsupported XZ stream flags");
		const checkId = flag1 & 0x0f;
		checkSize(checkId);
		const indexSize = (read32LE(bytes, footerStart + 4) + 1) * 4;
		if (!Number.isSafeInteger(indexSize) || indexSize > footerStart)
			throw new ArchiveError("Invalid XZ stream: backward index size is invalid");
		const indexStart = footerStart - indexSize;
		const records = parseIndex(bytes, indexStart, indexSize);
		let blocksSize = 0;
		for (const record of records) {
			blocksSize += Math.ceil(record.unpaddedSize / 4) * 4;
			if (!Number.isSafeInteger(blocksSize)) throw new ArchiveError("XZ stream uses sizes too large to read safely");
		}
		const start = indexStart - blocksSize - 12;
		if (start < 0 || start + 12 > bytes.byteLength || !equalBytes(bytes.subarray(start, start + 6), XZ_MAGIC)) {
			throw new ArchiveError("Invalid XZ stream: header position or magic is invalid");
		}
		if (bytes[start + 6] !== flag0 || bytes[start + 7] !== flag1)
			throw new ArchiveError("Invalid XZ stream: header and footer flags differ");
		if (crc32(bytes.subarray(start + 6, start + 8)) !== read32LE(bytes, start + 8))
			throw new ArchiveError("Invalid XZ stream: header CRC32 mismatch");
		streams.unshift({ start, indexStart, footerStart, checkId, records });
		end = start;
		void padding;
	}
	return streams;
}

interface XzFilter {
	id: number;
	properties: Uint8Array;
}

function deltaDecode(bytes: Uint8Array, distance: number): void {

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the file is genuine .xz (magic bytes at each stream start) and passes xz -t
  2. Re-download/restore the file to fix mid-stream corruption
  3. If concatenating streams, ensure exact stream boundaries with only 4-byte zero padding between them
  4. Use the library on whole buffers rather than hand-cut slices so offsets are computed internally

Example fix

// before
const bytes = concat([streamA, junkBytes, streamB]);
await xzDecode(bytes);
// after
const bytes = concat([streamA, new Uint8Array(4), streamB]); // streams back-to-back; padding only
await xzDecode(bytes);
Defensive patterns

Strategy: try-catch

Validate before calling

function startsWithXzMagic(bytes: Uint8Array): boolean {
  const m = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00];
  return m.every((b, i) => bytes[i] === b);
}

Type guard

null

Try / catch

try {
  await xzDecode(bytes);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("header position or magic")) {
    throw new Error("XZ body does not match its footer/index — corrupt or spliced archive");
  }
  throw err;
}

Prevention

When it happens

Trigger: Corrupted block padding/sizes making the computed header offset land on non-magic bytes; concatenating streams with extra non-zero bytes that shift block boundaries; a buffer whose tail parses as a plausible footer+index but whose body is not XZ data (e.g. random or other-format bytes).

Common situations: Opening files that merely resemble xz (renamed archives), splicing/multiplexing streams incorrectly, corruption in the block area of an otherwise intact footer/index.

Related errors


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