{"record":{"id":"3a22d3c2ef441252","repo":"can1357/oh-my-pi","slug":"unable-to-read-lzh-archive-error-instanceof-err","errorCode":null,"errorMessage":"Unable to read LZH archive: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Unable to read LZH archive: (.+?)","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":610,"sourceCode":"/** Probe whether bytes begin with an LZH/LHA member header. */\nexport function sniffLzh(bytes: Uint8Array): boolean {\n\tif (bytes.byteLength < 22 || bytes[2] !== 0x2d || bytes[6] !== 0x2d || bytes[3] !== 0x6c) return false;\n\tconst method = String.fromCharCode(...bytes.subarray(2, 7));\n\treturn LHA_METHOD_PATTERN.test(method) && bytes[20]! <= 2;\n}\n\n/** Index an LZH/LHA archive and lazily decode its members from the bounded archive buffer. */\nexport const readLzh: FormatReader = async (\n\tsource: ByteSource,\n\toptions: FormatReadOptions,\n): Promise<ArchiveIndexEntry[]> => {\n\tassertInMemorySize(source.size, options.limits);\n\tlet bytes: Uint8Array;\n\ttry {\n\t\tbytes = await readAllBytes(source);\n\t} catch (error) {\n\t\tif (error instanceof ArchiveError) throw error;\n\t\tthrow new ArchiveError(`Unable to read LZH archive: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (bytes.byteLength !== source.size) throw new ArchiveError(\"Invalid LZH archive: truncated data\");\n\tif (!sniffLzh(bytes)) throw new ArchiveError(\"Invalid LZH archive header\");\n\tconst entries: ArchiveIndexEntry[] = [];\n\tlet offset = 0;\n\tlet parsedCount = 0;\n\tlet metadataSize = 0;\n\twhile (offset < bytes.byteLength && bytes[offset] !== 0) {\n\t\tconst header = parseLzhHeader(bytes, offset, options);\n\t\tmetadataSize += header.dataStart - offset;\n\t\tassertIndexSize(metadataSize, options.limits, \"index\");\n\t\tassertEntryCount(++parsedCount, options.limits);\n\t\tif (header.nextOffset <= offset) throw new ArchiveError(\"Invalid LZH archive: header did not advance\");\n\t\toffset = header.nextOffset;\n\t\tif (!header.path) continue;\n\t\tconst isDirectory = header.method === \"-lhd-\";\n\t\tif (isDirectory && header.mode !== undefined && (header.mode & 0xf000) === 0xa000) {\n\t\t\tconst separator = header.path.indexOf(\"|\");","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L592-L628","documentation":"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.","triggerScenarios":"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.","commonSituations":"File moved/deleted between stat and read; insufficient permissions; reading from a flaky network mount or removable drive; a custom ByteSource implementation that throws.","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."],"exampleFix":"// before\nconst entries = await readLzh(source, options);\n// after\ntry {\n  const entries = await readLzh(source, options);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.startsWith(\"Unable to read LZH archive: \")) {\n    console.error(\"check file exists / permissions:\", err.message);\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"const st = await fs.stat(path);\nif (!st.isFile()) throw new Error(\"not a regular file\");\nawait fs.access(path, fs.constants.R_OK); // fail early on permissions","typeGuard":null,"tryCatchPattern":"try {\n  const entries = await readLzh(source, options);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.startsWith(\"Unable to read LZH archive:\")) {\n    // wrapped I/O error: message carries the original cause (e.g. EACCES, EIO)\n    logger.error(\"LZH source read failed\", { cause: err.message });\n    throw err;\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["io","archive","lzh","file-read","permissions"],"backgroundTag":"file-read-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}