can1357/oh-my-pi · error · ArchiveError

ASAR member '${formatArchivePathForError(memberPath)}' is tr

Error message

ASAR member '${formatArchivePathForError(memberPath)}' is truncated

What it means

After a successful source read, the reader checks that the number of bytes actually returned equals the member's header-recorded size. Fewer bytes means the archive data region ends before this member does — the ASAR is truncated — so the reader refuses to hand back partial data and skip the integrity check.

Source

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

		this.#size = size;
		this.#integrity = integrity;
	}

	async read(size: number, memberPath: string): Promise<Uint8Array> {
		if (size !== this.#size) {
			throw new ArchiveError(`ASAR member '${formatArchivePathForError(memberPath)}' has an inconsistent size`);
		}
		let bytes: Uint8Array;
		try {
			bytes = await this.#source.read(this.#offset, this.#offset + this.#size);
		} catch (error) {
			if (error instanceof ArchiveError) throw error;
			throw new ArchiveError(
				`Failed to read ASAR member '${formatArchivePathForError(memberPath)}': ${describeError(error)}`,
			);
		}
		if (bytes.byteLength !== this.#size) {
			throw new ArchiveError(`ASAR member '${formatArchivePathForError(memberPath)}' is truncated`);
		}
		verifyIntegrity(bytes, this.#integrity, memberPath);
		return bytes;
	}
}

class UnpackedAsarMemberSource implements MemberSource {
	readonly #filePath?: string;
	readonly #size: number;
	readonly #integrity?: AsarIntegrity;

	constructor(filePath: string | undefined, size: number, integrity: AsarIntegrity | undefined) {
		this.#filePath = filePath;
		this.#size = size;
		this.#integrity = integrity;
	}

	async read(size: number, memberPath: string): Promise<Uint8Array> {

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download or re-copy the complete ASAR and compare byte length against the original's known size/hash.
  2. Check available disk space and that no writer still holds the file open mid-write before reading.
  3. Validate whole-file integrity (size vs. header's expected total length) before member reads and fail fast with your own clearer message.
  4. Repackage with the official asar tool if the packaging process itself was interrupted.

Example fix

// before
const bytes = await member.read(size, path); // throws "is truncated"
// after
const stat = await fs.stat(asarPath);
if (stat.size < expectedTotalFromHeader(asarPath)) {
  throw new Error("ASAR file incomplete, re-downloading");
}
const bytes = await member.read(size, path);
Defensive patterns

Strategy: validation

Validate before calling

import * as fs from "node:fs/promises";
async function assertAsarComplete(asarPath: string, headerTotalSize: number): Promise<void> {
	const stat = await fs.stat(asarPath);
	if (stat.size < headerTotalSize) {
		throw new Error(`ASAR truncated: ${stat.size} bytes on disk, header expects ${headerTotalSize}`);
	}
}

Type guard

function isCompleteAsar(statSize: number, headerTotalSize: number): boolean {
	return statSize >= headerTotalSize;
}

Try / catch

try {
	const bytes = await member.read(size, path);
} catch (e) {
	if (e instanceof ArchiveError && e.message.includes("is truncated")) {
		await reDownloadAsar(sourceUrl, asarPath); // truncated file cannot be repaired in place
		return readMemberRetry(path);
	}
	throw e;
}

Prevention

When it happens

Trigger: PackedAsarMemberSource.read gets bytes whose byteLength < the header-recorded #size — the source's byte range extends past the end of the backing file/buffer, i.e. the ASAR file was truncated after the header was written (and the header-consistency check at read(size,...) passed).

Common situations: Incomplete downloads or interrupted copies of the .asar file; disk-full during packaging; sync tools that partially uploaded the archive; reading while the file is still being written.

Related errors


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