can1357/oh-my-pi · error · ArchiveError

Failed to read ASAR archive: ${describeError(error)}

Error message

Failed to read ASAR archive: ${describeError(error)}

What it means

A catch-all wrapper around readAsarIndex: any error thrown while parsing the ASAR index that is NOT already an ArchiveError is re-wrapped as `Failed to read ASAR archive: <cause>`. It indicates an unexpected failure during header parsing (JSON handling, byte-source I/O, limits enforcement from underlying layers) rather than a diagnosed ASAR format problem.

Source

Thrown at packages/utils/src/ar/asar.ts:368

			isDirectory: false,
			size,
			mode,
			storage: {
				type: "member",
				source: new PackedAsarMemberSource(source, memberOffset, size, integrity),
			},
		});
	}
	return entries;
}

/** Read an Electron ASAR index while keeping packed and unpacked member payloads lazy. */
export const readAsar: FormatReader = async (source, options) => {
	try {
		return await readAsarIndex(source, options);
	} catch (error) {
		if (error instanceof ArchiveError) throw error;
		throw new ArchiveError(`Failed to read ASAR archive: ${describeError(error)}`);
	}
};

/** Whether bytes begin with a structurally plausible Electron ASAR Pickle header. */
export function sniffAsar(bytes: Uint8Array): boolean {
	if (bytes.byteLength < ASAR_JSON_OFFSET + 1) return false;
	const outerPayload = readUInt32LE(bytes, 0);
	const headerSize = readUInt32LE(bytes, 4);
	const innerPayload = readUInt32LE(bytes, 8);
	const jsonSize = readUInt32LE(bytes, 12);
	return (
		outerPayload === 4 &&
		headerSize >= ASAR_INNER_PREFIX_SIZE &&
		headerSize === innerPayload + 4 &&
		innerPayload === 4 + alignAsarPayload(jsonSize) &&
		jsonSize > 0 &&
		bytes[ASAR_JSON_OFFSET] === 0x7b
	);

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the embedded cause after 'Failed to read ASAR archive:' to identify the real failure
  2. If using a custom ByteSource, make it throw ArchiveError (or validate it returns exactly the requested byte ranges)
  3. Verify the .asar file is complete and readable at the byte-source level (size, permissions) before parsing

Example fix

// before
const src = { size: f.size, read: (a,b) => f.slice(a,b) }; // rejects with raw errors
// after
const src = { size: f.size, read: async (a,b) => { try { return new Uint8Array(await f.slice(a,b).arrayBuffer()); } catch (e) { throw new ArchiveError(`read failed: ${e}`); } } };
Defensive patterns

Strategy: try-catch

Validate before calling

const bytes = await Bun.file(asarPath).slice(0, 16).bytes();
if (!sniffAsar(bytes)) throw new Error("not a valid ASAR archive");

Type guard

const looksLikeAsar = (bytes) => sniffAsar(bytes);

Try / catch

try { return await readAsar(source, options); } catch (e) { if (e instanceof ArchiveError) { logger.error("asar open failed", { cause: e.message }); } throw e; }

Prevention

When it happens

Trigger: The ByteSource.read implementation rejects (disk error, network stream failure); a JSON.parse or TextDecoder throwing outside the guarded spots; a limit-check helper throwing a non-ArchiveError; any runtime exception in the index walker.

Common situations: Custom ByteSource implementations that reject with native errors instead of ArchiveError; memory failures on very large headers; bugs in downstream limit helpers.

Related errors


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