can1357/oh-my-pi · error · ArchiveError
Invalid LZH archive: truncated data
Error message
Invalid LZH archive: truncated data
What it means
After reading all bytes, the library verifies the buffer length equals the ByteSource's declared size. A mismatch means the source shrank or reported a wrong size — the archive is incomplete relative to its own metadata, so parsing would read out of bounds.
Source
Thrown at packages/utils/src/ar/lzh.ts:612
if (bytes.byteLength < 22 || bytes[2] !== 0x2d || bytes[6] !== 0x2d || bytes[3] !== 0x6c) return false;
const method = String.fromCharCode(...bytes.subarray(2, 7));
return LHA_METHOD_PATTERN.test(method) && bytes[20]! <= 2;
}
/** Index an LZH/LHA archive and lazily decode its members from the bounded archive buffer. */
export const readLzh: FormatReader = async (
source: ByteSource,
options: FormatReadOptions,
): Promise<ArchiveIndexEntry[]> => {
assertInMemorySize(source.size, options.limits);
let bytes: Uint8Array;
try {
bytes = await readAllBytes(source);
} catch (error) {
if (error instanceof ArchiveError) throw error;
throw new ArchiveError(`Unable to read LZH archive: ${error instanceof Error ? error.message : String(error)}`);
}
if (bytes.byteLength !== source.size) throw new ArchiveError("Invalid LZH archive: truncated data");
if (!sniffLzh(bytes)) throw new ArchiveError("Invalid LZH archive header");
const entries: ArchiveIndexEntry[] = [];
let offset = 0;
let parsedCount = 0;
let metadataSize = 0;
while (offset < bytes.byteLength && bytes[offset] !== 0) {
const header = parseLzhHeader(bytes, offset, options);
metadataSize += header.dataStart - offset;
assertIndexSize(metadataSize, options.limits, "index");
assertEntryCount(++parsedCount, options.limits);
if (header.nextOffset <= offset) throw new ArchiveError("Invalid LZH archive: header did not advance");
offset = header.nextOffset;
if (!header.path) continue;
const isDirectory = header.method === "-lhd-";
if (isDirectory && header.mode !== undefined && (header.mode & 0xf000) === 0xa000) {
const separator = header.path.indexOf("|");
if (separator < 1) throw new ArchiveError(`Invalid LZH symbolic link '${header.path}'`);
const path = normalizeArchiveEntryPath(header.path.slice(0, separator));View on GitHub (pinned to 9690622007)
Solutions
- Re-download or re-copy the archive and confirm the size matches the origin.
- Rebuild the ByteSource from a fresh stat so size matches current file length.
- Avoid reading archives while they are still being written; wait for the producer to finish (e.g. tmpfile + rename pattern).
- Verify the archive's integrity with a checksum before indexing.
Example fix
// before: size captured earlier, file changed since const source = makeByteSource(path, staleSize); const entries = await readLzh(source, options); // after: refresh size immediately before reading const size = (await fs.stat(path)).size; const source = makeByteSource(path, size); const entries = await readLzh(source, options);
Defensive patterns
Strategy: validation
Validate before calling
const st = await fs.stat(path);
const bytes = await readAllBytes(source);
if (bytes.length !== st.size) throw new Error("file changed or truncated during read — retry with a fresh stat"); Try / catch
try {
const entries = await readLzh(source, options);
} catch (err) {
if (err instanceof ArchiveError && err.message === "Invalid LZH archive: truncated data") {
throw new Error("archive incomplete — re-download or wait for the writer to finish");
}
throw err;
} Prevention
- Write archives to a temp file and atomically rename when complete
- Never index archives while a producer is still writing them
- Rebuild the ByteSource from a fresh stat immediately before reading
When it happens
Trigger: readLzh() where readAllBytes() returns fewer bytes than source.size: file truncated during download/copy, concurrent writer shrinking the file, ByteSource.size stale after the file changed, partial transfer.
Common situations: Interrupted downloads; rsync/ftp copy cut short; another process rewriting the .lzh while it is being indexed; custom ByteSource built from a stale stat().
Related errors
- ASAR member '${formatArchivePathForError(memberPath)}' is tr
- Unable to read LZH archive: ${error instanceof Error ? error
- Invalid ar archive: truncated ${what}
- Unable to read ARJ archive: ${error instanceof Error ? error
- Invalid ARJ archive: truncated data
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/775bd93ed4451547.
Report an issue: GitHub.