{"record":{"id":"bd4eaeec4063a1bf","repo":"can1357/oh-my-pi","slug":"invalid-ar-archive-truncated-member-header","errorCode":null,"errorMessage":"Invalid ar archive: truncated member header","messagePattern":"Invalid ar archive: truncated member header","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/unix-ar.ts","lineNumber":202,"sourceCode":"\t\tassertEntryCount(entries.size, options.limits);\n\t}\n\tensureParentDirectories(entries, options.limits);\n\treturn [...entries.values()];\n}\n\nfunction readSignatureFromBuffer(bytes: Uint8Array): void {\n\tif (!sniffUnixAr(bytes)) throw new ArchiveError(\"Invalid ar archive signature\");\n}\n\n/** Parse a fully materialized Unix ar archive for composition by formats such as deb. */\nexport function readUnixArEntriesFromBuffer(bytes: Uint8Array, options: FormatReadOptions): ArchiveIndexEntry[] {\n\treadSignatureFromBuffer(bytes);\n\tconst records: RawArMember[] = [];\n\tlet longNames: Uint8Array | undefined;\n\tlet metadataSize = 0;\n\tfor (let position = SIGNATURE.length; position < bytes.byteLength; ) {\n\t\tif (bytes.byteLength - position < HEADER_SIZE)\n\t\t\tthrow new ArchiveError(\"Invalid ar archive: truncated member header\");\n\t\tconst header = parseHeader(readMemoryRange(bytes, position, position + HEADER_SIZE));\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 > bytes.byteLength) {\n\t\t\tthrow new ArchiveError(\"Invalid ar archive: truncated member data\");\n\t\t}\n\t\tlet name = header.rawName;\n\t\tlet nameByteLength = Buffer.byteLength(name, \"utf-8\");\n\t\tlet dataOffset = payloadOffset;\n\t\tlet size = header.physicalSize;\n\t\tif (header.bsdNameLength !== undefined) {\n\t\t\tmetadataSize += header.bsdNameLength;\n\t\t\tassertIndexSize(metadataSize, options.limits, \"index\");\n\t\t\tconst nameBytes = readMemoryRange(bytes, payloadOffset, payloadOffset + header.bsdNameLength);\n\t\t\tconst decoded = decodeBsdName(nameBytes, options.limits);\n\t\t\tname = decoded.name;","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/unix-ar.ts#L184-L220","documentation":"While scanning members, fewer than HEADER_SIZE (60) bytes remained before end-of-input, so a complete member header could not be read. The library treats a header that cannot be fully present as a structurally invalid archive.","triggerScenarios":"Trailing garbage under 60 bytes, or a truncated archive whose last member header is cut off mid-way; the loop position advances past payloadEnd and the remaining tail is shorter than 60 bytes.","commonSituations":"Incomplete downloads (FTP/HTTP cut short); archives appended with a partial member; disk-full during archive creation; a file that had the signature but almost no members.","solutions":["Re-acquire the archive and verify its size/hash; the file is truncated.","If the source is a stream, ensure the whole file was written before parsing.","Re-create the archive with ar; test it with `ar t` first.","If you must read partially downloaded archives, pre-check `bytes.byteLength` sanity or use range requests to fetch complete data."],"exampleFix":"// before: parsing a partially downloaded file\nconst entries = await readUnixAr(partialBuffer);\n// after: confirm completeness\nif (actualSize !== expectedSize) throw new Error('incomplete download');\nconst entries = await readUnixAr(fullBuffer);","handlingStrategy":"validation","validationCode":"if (bytes.byteLength < 8) throw new Error('not an ar archive');\n// tail safety: ensure any trailing data is a multiple-free but header-sized or absent\nconst tail = bytes.byteLength - 8;\nif (tail > 0 && tail < 60 && !allowTrailingGarbage) throw new Error('truncated trailing header');","typeGuard":null,"tryCatchPattern":"try {\n  return await readUnixArEntriesFromBuffer(bytes, opts);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('truncated member header')) {\n    throw new Error('archive incomplete: re-download and verify checksum');\n  }\n  throw err;\n}","preventionTips":["Verify file size against an expected/published size before parsing.","Write archives atomically (temp file + rename) so partial files are never observed.","Check HTTP Content-Length vs received bytes on download.","Treat sub-60-byte tails as corruption at ingest time."],"tags":["archive","ar-format","truncated-file","parsing"],"backgroundTag":"truncated-archive-member-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}