can1357/oh-my-pi · error · ArchiveError

Invalid ZIP archive: Unicode path extra field is too small

Error message

Invalid ZIP archive: Unicode path extra field is too small

What it means

Thrown by parseExtra when an extra field with id 0x7075 (Info-ZIP Unicode Path) is present but its data is shorter than the minimum 5 bytes (version byte + 4-byte CRC32 of the original name). A valid Unicode Path record must carry at least the version and checksum before any UTF-8 name payload.

Source

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

	const value = new Date(year, month, day, hour, minute, second).getTime();
	return Number.isFinite(value) ? value : undefined;
}

function parseExtra(extra: Uint8Array, rawName: Uint8Array): ParsedExtra {
	const result: ParsedExtra = {};
	let offset = 0;
	while (offset < extra.byteLength) {
		if (offset + 4 > extra.byteLength) throw new ArchiveError("Invalid ZIP archive: truncated extra-field header");
		const id = readUInt16LE(extra, offset);
		const size = readUInt16LE(extra, offset + 2);
		const dataStart = offset + 4;
		const dataEnd = dataStart + size;
		if (dataEnd > extra.byteLength) throw new ArchiveError("Invalid ZIP archive: malformed extra field");
		const data = extra.subarray(dataStart, dataEnd);
		if (id === 0x0001) {
			result.zip64 = data;
		} else if (id === 0x7075) {
			if (data.byteLength < 5) throw new ArchiveError("Invalid ZIP archive: Unicode path extra field is too small");
			if (data[0] === 1 && readUInt32LE(data, 1) === crc32(rawName)) {
				try {
					result.unicodePath = UTF8_FATAL_DECODER.decode(data.subarray(5));
				} catch {
					// A bad optional Unicode path falls back to the header name.
				}
			}
		} else if (id === 0x5455) {
			if (data.byteLength < 1)
				throw new ArchiveError("Invalid ZIP archive: extended timestamp extra field is too small");
			if ((data[0]! & 1) !== 0) {
				if (data.byteLength < 5)
					throw new ArchiveError("Invalid ZIP archive: extended timestamp extra field is too small");
				result.mtimeMs = (readUInt32LE(data, 1) | 0) * 1000;
			}
		} else if (id === 0x000a && result.mtimeMs === undefined) {
			result.mtimeMs = parseNtfsMtime(data);
		}

View on GitHub (pinned to 9690622007)

Solutions

  1. Test with `unzip -t`; standard tools may tolerate it, but rebuild anyway: `unzip` then `zip -r clean.zip .`.
  2. Recreate the archive with Info-ZIP, 7-Zip, or Python zipfile so Unicode paths are encoded per spec (or via the UTF-8 flag instead of 0x7075).
  3. Repair with `zip -FF` if the field region was corrupted.
  4. If you control the writer, emit version=1 + CRC32(name) + UTF-8 name, or set the general-purpose UTF-8 flag (0x0800) instead.

Example fix

// before: reading output of a custom writer emitting bare 0x7075 records
const zip = readZip(customUnicodeZip);
// after: rebuild so Unicode names use the UTF-8 flag
await $`unzip -q customUnicode.zip -d tmp`;
await $`zip -qr clean.zip .`.cwd("tmp"); // Info-ZIP sets UTF-8 flag / proper 0x7075
const zip = readZip("clean.zip");
Defensive patterns

Strategy: try-catch

Validate before calling

const probe = Bun.spawnSync(["unzip", "-t", path]);
if (probe.exitCode !== 0) throw new Error(`archive invalid; rebuild with a standard zip tool before reading: ${path}`);

Try / catch

try {
  const zip = readZip(path);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("Unicode path extra field is too small")) {
    // producer's Unicode-path extras are nonstandard: re-zip to normalize
    await $`unzip -q ${path} -d tmp`; await $`zip -qr clean.zip .`.cwd("tmp");
    return readZip("clean.zip");
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling zip info/read on an archive whose entries include a 0x7075 extra record with data length < 5 — produced by a writer that emits the record header/id without the required version+CRC prefix.

Common situations: Archives from nonstandard or homegrown zip writers attempting Unicode filename support; archives with extra fields rewritten by lossy tooling; fuzzed zips.

Related errors


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