can1357/oh-my-pi · error · ArchiveError
Unable to read LZH archive: ${error instanceof Error ? error
Error message
Unable to read LZH archive: ${error instanceof Error ? error.message : String(error)} What it means
readLzh() must load the whole archive into memory via readAllBytes(source). If that read fails for a non-ArchiveError reason (I/O error, permission problem, stream failure), the underlying error is wrapped into this ArchiveError so callers only need to handle one error type.
Source
Thrown at packages/utils/src/ar/lzh.ts:610
/** Probe whether bytes begin with an LZH/LHA member header. */
export function sniffLzh(bytes: Uint8Array): boolean {
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("|");View on GitHub (pinned to 9690622007)
Solutions
- Inspect the wrapped message (it contains the original error text, e.g. EACCES) and fix the underlying cause.
- Check file permissions and that the path still exists before calling readLzh.
- Re-try transient I/O failures (network mounts, removable media) once the device is available.
- If using a custom ByteSource, verify its read implementation handles errors and streams correctly.
Example fix
// before
const entries = await readLzh(source, options);
// after
try {
const entries = await readLzh(source, options);
} catch (err) {
if (err instanceof ArchiveError && err.message.startsWith("Unable to read LZH archive: ")) {
console.error("check file exists / permissions:", err.message);
}
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
const st = await fs.stat(path);
if (!st.isFile()) throw new Error("not a regular file");
await fs.access(path, fs.constants.R_OK); // fail early on permissions Try / catch
try {
const entries = await readLzh(source, options);
} catch (err) {
if (err instanceof ArchiveError && err.message.startsWith("Unable to read LZH archive:")) {
// wrapped I/O error: message carries the original cause (e.g. EACCES, EIO)
logger.error("LZH source read failed", { cause: err.message });
throw err;
}
throw err;
} Prevention
- Check readability and existence of the file before constructing the ByteSource
- Avoid reading from network/removable mounts without retry logic
- Keep a single open-and-read window; don't stat early and read much later
When it happens
Trigger: readLzh() with a ByteSource whose underlying read fails: unreadable file, deleted file mid-read, broken stream, network-backed source erroring, EACCES/EIO from the filesystem.
Common situations: File moved/deleted between stat and read; insufficient permissions; reading from a flaky network mount or removable drive; a custom ByteSource implementation that throws.
Related errors
- Invalid LZH Unix permissions extended header
- Invalid LZH archive: truncated data
- {}: {error}
- error reading {}: {}
- Permission denied
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/3a22d3c2ef441252.
Report an issue: GitHub.