can1357/oh-my-pi · error · ArchiveError
ASAR member '${label}' has an inconsistent size
Error message
ASAR member '${label}' has an inconsistent size What it means
This ArchiveError is thrown by UnpackedAsarMemberSource.read when the `size` argument passed by the archive framework does not match the member size recorded in the ASAR header. It is an internal consistency check: the caller must request exactly the byte count the index declares for this file. A mismatch means either the caller passed a wrong size or the archive index was built from inconsistent metadata.
Source
Thrown at packages/utils/src/ar/asar.ts:150
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> {
const label = formatArchivePathForError(memberPath);
if (size !== this.#size) {
throw new ArchiveError(`ASAR member '${label}' has an inconsistent size`);
}
if (!this.#filePath) {
throw new ArchiveError(`Archive file '${label}' is unpacked and requires a filesystem-backed ASAR archive`);
}
const file = Bun.file(this.#filePath);
const stat = await file.stat().catch(() => {
throw new ArchiveError(`Unpacked ASAR file '${label}' was not found`);
});
if (stat.isDirectory()) {
throw new ArchiveError(`Unpacked ASAR file '${label}' is a directory`);
}
if (stat.size !== this.#size) {
throw new ArchiveError(
`Unpacked ASAR file '${label}' size differs from its archive header (${stat.size} != ${this.#size} bytes)`,
);
}
let bytes: Uint8Array;
try {View on GitHub (pinned to 9690622007)
Solutions
- Ensure the size passed to read() comes from the same ArchiveIndexEntry the MemberSource was created from (entry.size), not a separately computed value
- Re-read the archive index so entry metadata and member sources are in sync
- Check whether custom code caches entry sizes across re-parses and clear that cache
Example fix
// before const bytes = await member.read(myEstimatedSize, entry.path); // after const bytes = await member.read(entry.size, entry.path);
Defensive patterns
Strategy: validation
Validate before calling
if (requestedSize !== entry.size) throw new Error(`size mismatch for ${entry.path}: ${requestedSize} != ${entry.size}`);
await member.read(entry.size, entry.path); Type guard
function isEntrySize(entry, size) { return size === entry.size; } Try / catch
try { bytes = await member.read(entry.size, entry.path); } catch (e) { if (e instanceof ArchiveError && e.message.includes("inconsistent size")) { /* re-read fresh index and retry */ } else throw e; } Prevention
- Always source the read size from the same entry object that owns the member source
- Do not cache sizes separately from the archive index
- Re-parse the index rather than reusing stale metadata after archive changes
When it happens
Trigger: Calling read() on an unpacked ASAR member entry with a size argument different from the entry's declared size in the ASAR JSON header (e.g. reading with a stale size after the index was re-parsed, or a framework caller computing size from the wrong entry).
Common situations: Bugs in code that resolves archive entries by path and passes the wrong entry's size; custom tooling that manually reads members instead of going through the archive API; corrupted or hand-edited ASAR headers.
Related errors
- ASAR member '${formatArchivePathForError(memberPath)}' faile
- ASAR member '${formatArchivePathForError(memberPath)}' has a
- Failed to read ASAR member '${formatArchivePathForError(memb
- ASAR member '${formatArchivePathForError(memberPath)}' is tr
- ASAR member path '${memberPath}' crosses file '${parts.slice
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2fd678a2e8388878.
Report an issue: GitHub.