{"record":{"id":"bbe9f92d24eb92b7","repo":"can1357/oh-my-pi","slug":"error-instanceof-error-error-message-string-bbe9f9","errorCode":null,"errorMessage":"${error instanceof Error ? error.message : String(error)}","messagePattern":"\\$\\{error instanceof Error \\? error\\.message : String\\(error\\)\\}","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/unix-ar.ts","lineNumber":247,"sourceCode":"\t\trecords.push({ name, nameByteLength, dataOffset, size, mtimeSeconds: header.mtimeSeconds, mode: header.mode });\n\t\tassertEntryCount(records.length, options.limits);\n\t\tposition = payloadEnd + (header.physicalSize & 1);\n\t\tif (position > bytes.byteLength) throw new ArchiveError(\"Invalid ar archive: missing alignment byte\");\n\t}\n\treturn materializeEntries(records, longNames, memoryByteSource(bytes), options);\n}\n\nasync function readExact(source: ByteSource, start: number, end: number, what: string): Promise<Uint8Array> {\n\tif (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > source.size) {\n\t\tthrow new ArchiveError(`Invalid ar archive: truncated ${what}`);\n\t}\n\ttry {\n\t\tconst bytes = await source.read(start, end);\n\t\tif (bytes.byteLength !== end - start) throw new ArchiveError(`Invalid ar archive: truncated ${what}`);\n\t\treturn bytes;\n\t} catch (error) {\n\t\tif (error instanceof ArchiveError) throw error;\n\t\tthrow new ArchiveError(error instanceof Error ? error.message : String(error));\n\t}\n}\n\nasync function readUnixArImpl(source: ByteSource, options: FormatReadOptions): Promise<ArchiveIndexEntry[]> {\n\tif (!Number.isSafeInteger(source.size) || source.size < SIGNATURE.length)\n\t\tthrow new ArchiveError(\"Invalid ar archive signature\");\n\treadSignatureFromBuffer(await readExact(source, 0, SIGNATURE.length, \"signature\"));\n\tconst records: RawArMember[] = [];\n\tlet longNames: Uint8Array | undefined;\n\tlet metadataSize = 0;\n\tfor (let position = SIGNATURE.length; position < source.size; ) {\n\t\tconst headerBytes = await readExact(source, position, position + HEADER_SIZE, \"member header\");\n\t\tconst header = parseHeader(headerBytes);\n\t\tmetadataSize += HEADER_SIZE;\n\t\tassertIndexSize(metadataSize, options.limits, \"index\");\n\t\tconst payloadOffset = position + HEADER_SIZE;\n\t\tconst payloadEnd = payloadOffset + header.physicalSize;\n\t\tif (!Number.isSafeInteger(payloadEnd) || payloadEnd > source.size) {","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/unix-ar.ts#L229-L265","documentation":"readExact wraps source.read in try/catch: if the read fails with anything that is not already an ArchiveError (network failure, permission error, decode error, etc.), the original message is re-wrapped in ArchiveError so all archive-reading failures share one error type. The message text is the underlying error's message.","triggerScenarios":"source.read(start,end) throws — e.g. EACCES on the file, an HTTP 4xx/5xx from a range request, an abort, or a TypeError from a bad custom ByteSource implementation.","commonSituations":"Permission errors on protected files; expired auth tokens for remote sources; misimplemented ByteSource (read signature mismatch, wrong this-binding); transient network drops.","solutions":["Read the wrapped message — it is the original cause; fix that underlying problem.","Check file permissions / re-authenticate before parsing remote archives.","Test your custom ByteSource: read(start,end) must return a Uint8Array of exactly end-start bytes.","Catch ArchiveError uniformly and inspect message/cause instead of handling raw source errors."],"exampleFix":"// before: wrong custom source\nread(start, end) { return this.buf.slice(start, end); } // may be short/throw\n// after\nasync read(start: number, end: number): Promise<Uint8Array> {\n  if (end > this.size) throw new ArchiveError(`range past EOF`);\n  return this.buf.subarray(start, end);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isArchiveError(err: unknown): err is ArchiveError {\n  return err instanceof ArchiveError;\n}","tryCatchPattern":"try {\n  return await readUnixAr(source);\n} catch (err) {\n  if (isArchiveError(err)) {\n    logger.error('ar parse failed', { message: err.message }); // message = root cause\n    throw new UserFacingError(`could not read archive: ${err.message}`);\n  }\n  throw err;\n}","preventionTips":["Check permissions/auth on the byte source before parsing (EACCES and 401s surface through this wrap).","Keep the underlying error message in logs — it is the real cause.","Type-test custom ByteSource implementations (correct read signature, no lost `this`).","Do not swallow non-ArchiveError throws from your own source.read; fix them at the source."],"tags":["archive","error-wrapping","io","error-handling"],"backgroundTag":"archive-source-read-failure","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}