can1357/oh-my-pi · error · ArchiveError
Invalid ARJ archive: truncated ${what}
Error message
Invalid ARJ archive: truncated ${what} What it means
assertRange in the ARJ archive reader verifies that a [start, end) byte window lies entirely within the loaded archive bytes. If the offsets are non-integers, negative, inverted, or extend past the buffer, it throws ArchiveError('Invalid ARJ archive: truncated <what>'), where <what> names the structure being read (header signature, basic header, extended header size, local file header, etc.).
Source
Thrown at packages/utils/src/ar/arj.ts:30
const LEGACY_DECODER = new TextDecoder("windows-1252");
function u16(bytes: Uint8Array, offset: number): number {
return bytes[offset]! | (bytes[offset + 1]! << 8);
}
function u32(bytes: Uint8Array, offset: number): number {
return (bytes[offset]! | (bytes[offset + 1]! << 8) | (bytes[offset + 2]! << 16) | (bytes[offset + 3]! << 24)) >>> 0;
}
function assertRange(bytes: Uint8Array, start: number, end: number, what: string): void {
if (
!Number.isSafeInteger(start) ||
!Number.isSafeInteger(end) ||
start < 0 ||
end < start ||
end > bytes.byteLength
) {
throw new ArchiveError(`Invalid ARJ archive: truncated ${what}`);
}
}
function dosTimeToMs(value: number): number | undefined {
if (value === 0) return undefined;
const year = 1980 + ((value >>> 25) & 0x7f);
const month = (value >>> 21) & 0x0f;
const day = (value >>> 16) & 0x1f;
const hour = (value >>> 11) & 0x1f;
const minute = (value >>> 5) & 0x3f;
const second = (value & 0x1f) * 2;
if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59) return undefined;
return Date.UTC(year, month - 1, day, hour, minute, second);
}
function readCString(bytes: Uint8Array, start: number, end: number, field: string): { value: string; next: number } {
let terminator = start;
while (terminator < end && bytes[terminator] !== 0) terminator++;View on GitHub (pinned to 9690622007)
Solutions
- Re-acquire the archive and verify its size/checksum against the original source.
- Check the full file was passed to the reader (not a truncated subarray) and that the download/copy completed.
- Validate the archive with an external tool to confirm it is corrupt before deeper debugging.
- If the source is a stream, ensure the reader gets all bytes (readAllBytes) rather than a first-chunk buffer.
Example fix
// before
const bytes = (await Bun.file("data.arj").arrayBuffer()).slice(0, 1024); // truncated
readArj(new Uint8Array(bytes));
// after
const bytes = new Uint8Array(await Bun.file("data.arj").arrayBuffer());
try { readArj(bytes); } catch (e) { /* ArchiveError: truncated ... */ } Defensive patterns
Strategy: try-catch
Validate before calling
if (bytes.byteLength < 4 || bytes[0] !== 0x60 || bytes[1] !== 0xea) {
throw new Error("Not an ARJ archive (missing 0x60 0xEA signature)");
} Type guard
function looksLikeArj(bytes: Uint8Array): boolean {
return bytes.byteLength >= 4 && bytes[0] === 0x60 && bytes[1] === 0xea;
} Try / catch
import { ArchiveError } from "@oh-my-pi/pi-utils/ar/ar/error";
try {
return readArj(bytes);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("truncated")) {
throw new Error(`ARJ file incomplete (${bytes.byteLength} bytes read); re-download the archive`);
}
throw err;
} Prevention
- Verify file size and checksum against the source before extracting.
- Never feed the reader a subarray/slice that may cut the archive short.
- Ensure downloads/transfers complete (Content-Length check) before parsing.
- Treat DOS-era .arj files as suspect: detect truncation early via the trailing end-of-archive marker.
When it happens
Trigger: Calling readArj/parseArjBlock on a byte buffer that ends mid-structure: a partially downloaded or copied .arj file, an archive truncated by a failed transfer, a header whose declared size exceeds remaining bytes, or reading from a short buffer where offset arithmetic overruns byteLength.
Common situations: Incomplete FTP/HTTP download of an .arj archive; a file truncated at the source (bad floppy/disk recovery of a DOS-era archive); passing a Uint8Array subarray/slice that cut off the tail; corrupted archive headers with bogus size fields.
Related errors
- Invalid ARJ local header
- Invalid ARJ ${field}: missing terminator
- Invalid ARJ basic header size
- Invalid ARJ basic header CRC32
- Invalid ARJ archive: too many extended headers
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ab62d966ee8a76ed.
Report an issue: GitHub.