{"record":{"id":"c4466706e7d8ef81","repo":"can1357/oh-my-pi","slug":"error-message-c44667","errorCode":null,"errorMessage":"${error.message}","messagePattern":"\\$\\{error\\.message\\}","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/unix-ar.ts","lineNumber":43,"sourceCode":"class ArMemberSource implements MemberSource {\n\treadonly #source: ByteSource;\n\treadonly #offset: number;\n\n\tconstructor(source: ByteSource, offset: number) {\n\t\tthis.#source = source;\n\t\tthis.#offset = offset;\n\t}\n\n\tasync read(size: number, memberPath: string): Promise<Uint8Array> {\n\t\ttry {\n\t\t\tconst bytes = await this.#source.read(this.#offset, this.#offset + size);\n\t\t\tif (bytes.byteLength !== size) {\n\t\t\t\tthrow new ArchiveError(`Archive member '${memberPath}' is truncated`);\n\t\t\t}\n\t\t\treturn bytes;\n\t\t} catch (error) {\n\t\t\tif (error instanceof ArchiveError) throw error;\n\t\t\tthrow new ArchiveError(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n}\n\nfunction decodeAsciiField(bytes: Uint8Array, offset: number, length: number): string {\n\tlet end = offset + length;\n\twhile (end > offset && bytes[end - 1] === 0x20) end--;\n\tlet value = \"\";\n\tfor (let index = offset; index < end; index++) {\n\t\tconst byte = bytes[index]!;\n\t\tif (byte < 0x20 || byte > 0x7e) throw new ArchiveError(\"Invalid ar archive header field\");\n\t\tvalue += String.fromCharCode(byte);\n\t}\n\treturn value;\n}\n\nfunction parseOptionalNumber(value: string, radix: 8 | 10, field: string): number | undefined {\n\tif (value === \"\" || value === \"-1\") return undefined;","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/unix-ar.ts#L25-L61","documentation":"In ArReader.read, any non-ArchiveError thrown by the underlying source (I/O errors, decode failures, network errors from a remote source) is caught and re-thrown as an ArchiveError whose message is the original error's message. You see '<original message>' with no additional wrapping.","triggerScenarios":"The backing source's read() rejects — e.g. ENOENT/EISDIR on a file source, a closed stream, a network abort on an HTTP-range source, or a permissions error during the read.","commonSituations":"Reading an ar member after the file was deleted or the handle closed; a remote source returning an error body; permissions changes mid-session.","solutions":["Inspect error.message (and cause) to identify the underlying I/O failure and fix that condition","Wrap read() in try-catch and treat ArchiveError as the unified error type","Re-open or re-acquire the source before retrying"],"exampleFix":"// before\nconst bytes = await reader.read(size, name);\n// after\ntry {\n\tconst bytes = await reader.read(size, name);\n} catch (err) {\n\tif (err instanceof ArchiveError) {\n\t\t// original message preserved in err.message\n\t}\n\tthrow err;\n}","handlingStrategy":"try-catch","validationCode":"try {\n\tawait fs.stat(archivePath);\n} catch (err) {\n\tthrow new Error(`Archive source unavailable: ${err instanceof Error ? err.message : err}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n\tconst data = await reader.read(size, name);\n} catch (err) {\n\tif (err instanceof ArchiveError) {\n\t\t// original cause is in err.message; log and handle I/O condition\n\t}\n\tthrow err;\n}","preventionTips":["Keep source handles/files alive while reading members","Handle ENOENT/EACCES on the archive path before member reads","For remote sources, use retry with range-request verification"],"tags":["archive","ar","error-wrapping"],"backgroundTag":"io-error-wrapped","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}