can1357/oh-my-pi · error · ArchiveError

${error instanceof Error ? error.message : String(error)}

Error message

${error instanceof Error ? error.message : String(error)}

What it means

readExact wraps source.read in try/catch: if the read fails with anything that is not already an ArchiveError (network failure, permission error, decode error, etc.), the original message is re-wrapped in ArchiveError so all archive-reading failures share one error type. The message text is the underlying error's message.

Source

Thrown at packages/utils/src/ar/unix-ar.ts:247

		records.push({ name, nameByteLength, dataOffset, size, mtimeSeconds: header.mtimeSeconds, mode: header.mode });
		assertEntryCount(records.length, options.limits);
		position = payloadEnd + (header.physicalSize & 1);
		if (position > bytes.byteLength) throw new ArchiveError("Invalid ar archive: missing alignment byte");
	}
	return materializeEntries(records, longNames, memoryByteSource(bytes), options);
}

async function readExact(source: ByteSource, start: number, end: number, what: string): Promise<Uint8Array> {
	if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > source.size) {
		throw new ArchiveError(`Invalid ar archive: truncated ${what}`);
	}
	try {
		const bytes = await source.read(start, end);
		if (bytes.byteLength !== end - start) throw new ArchiveError(`Invalid ar archive: truncated ${what}`);
		return bytes;
	} catch (error) {
		if (error instanceof ArchiveError) throw error;
		throw new ArchiveError(error instanceof Error ? error.message : String(error));
	}
}

async function readUnixArImpl(source: ByteSource, options: FormatReadOptions): Promise<ArchiveIndexEntry[]> {
	if (!Number.isSafeInteger(source.size) || source.size < SIGNATURE.length)
		throw new ArchiveError("Invalid ar archive signature");
	readSignatureFromBuffer(await readExact(source, 0, SIGNATURE.length, "signature"));
	const records: RawArMember[] = [];
	let longNames: Uint8Array | undefined;
	let metadataSize = 0;
	for (let position = SIGNATURE.length; position < source.size; ) {
		const headerBytes = await readExact(source, position, position + HEADER_SIZE, "member header");
		const header = parseHeader(headerBytes);
		metadataSize += HEADER_SIZE;
		assertIndexSize(metadataSize, options.limits, "index");
		const payloadOffset = position + HEADER_SIZE;
		const payloadEnd = payloadOffset + header.physicalSize;
		if (!Number.isSafeInteger(payloadEnd) || payloadEnd > source.size) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the wrapped message — it is the original cause; fix that underlying problem.
  2. Check file permissions / re-authenticate before parsing remote archives.
  3. Test your custom ByteSource: read(start,end) must return a Uint8Array of exactly end-start bytes.
  4. Catch ArchiveError uniformly and inspect message/cause instead of handling raw source errors.

Example fix

// before: wrong custom source
read(start, end) { return this.buf.slice(start, end); } // may be short/throw
// after
async read(start: number, end: number): Promise<Uint8Array> {
  if (end > this.size) throw new ArchiveError(`range past EOF`);
  return this.buf.subarray(start, end);
}
Defensive patterns

Strategy: try-catch

Type guard

function isArchiveError(err: unknown): err is ArchiveError {
  return err instanceof ArchiveError;
}

Try / catch

try {
  return await readUnixAr(source);
} catch (err) {
  if (isArchiveError(err)) {
    logger.error('ar parse failed', { message: err.message }); // message = root cause
    throw new UserFacingError(`could not read archive: ${err.message}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: source.read(start,end) throws — e.g. EACCES on the file, an HTTP 4xx/5xx from a range request, an abort, or a TypeError from a bad custom ByteSource implementation.

Common situations: Permission errors on protected files; expired auth tokens for remote sources; misimplemented ByteSource (read signature mismatch, wrong this-binding); transient network drops.

Related errors


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