{"record":{"id":"f9a0fa5a3c10c235","repo":"can1357/oh-my-pi","slug":"empty-ar-archive-long-name-at-offset-offset","errorCode":null,"errorMessage":"Empty ar archive long name at offset ${offset}","messagePattern":"Empty ar archive long name at offset (.+?)","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/unix-ar.ts","lineNumber":140,"sourceCode":"}\n\nfunction resolveLongName(\n\treference: string,\n\ttable: Uint8Array,\n\tlimits: ArchiveLimits,\n): { name: string; byteLength: number } {\n\tconst offsetText = reference.slice(1);\n\tif (!/^\\d+$/.test(offsetText)) throw new ArchiveError(`Invalid ar archive member name '${reference}'`);\n\tconst offset = Number.parseInt(offsetText, 10);\n\tif (!Number.isSafeInteger(offset) || offset < 0 || offset >= table.byteLength) {\n\t\tthrow new ArchiveError(`Invalid ar archive long-name offset '${reference}'`);\n\t}\n\tlet end = offset;\n\twhile (end < table.byteLength && table[end] !== 0 && table[end] !== 0x0a) end++;\n\tif (end === table.byteLength) throw new ArchiveError(`Unterminated ar archive long name at offset ${offset}`);\n\tlet nameEnd = end;\n\tif (table[end] === 0x0a && nameEnd > offset && table[nameEnd - 1] === 0x2f) nameEnd--;\n\tif (nameEnd === offset) throw new ArchiveError(`Empty ar archive long name at offset ${offset}`);\n\tconst nameBytes = table.subarray(offset, nameEnd);\n\treturn { name: decodeName(nameBytes, limits), byteLength: nameBytes.byteLength };\n}\n\nfunction isMetadataName(name: string): boolean {\n\treturn name === \"/\" || name === \"//\" || name === \"/SYM64/\" || name === \"__.SYMDEF\" || name === \"__.SYMDEF SORTED\";\n}\n\nfunction materializeEntries(\n\trecords: RawArMember[],\n\tlongNames: Uint8Array | undefined,\n\tsource: ByteSource,\n\toptions: FormatReadOptions,\n): ArchiveIndexEntry[] {\n\tconst entries = new Map<string, ArchiveIndexEntry>();\n\tfor (const record of records) {\n\t\tlet name = record.name;\n\t\tlet nameByteLength = record.nameByteLength;","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/unix-ar.ts#L122-L158","documentation":"resolveLongName extracts an entry name from the ar '//' long-name table. This error means the name slice at the requested offset was zero bytes long — the table contains an empty string where a filename was expected. The library treats a corrupt/empty long-name reference as a structural archive failure and refuses to guess.","triggerScenarios":"An archive member whose name is '/NNN' points at an offset in the long-name table that is either immediately followed by a terminator (offset at end of table, or an empty line), producing a zero-length name slice.","commonSituations":"Corrupted or truncated .deb/.a files during download; archives written by non-conforming tools that pad the long-name table incorrectly; hand-edited or binary-patched archives; fuzz/malicious inputs.","solutions":["Re-download or re-extract the archive — the long-name table is corrupt and usually the file itself is damaged.","Re-create the archive with ar/gcc-ar so the '//' table is regenerated correctly.","If you control the writer, ensure every '/NNN' offset points at a non-empty NUL/newline-terminated name (with a trailing '/' where applicable).","If you only need short-named members, skip or pre-filter entries with '/NNN' names before materializing."],"exampleFix":"// before: trusting an untrusted .deb\nconst entries = await readUnixAr(file);\n// after: validate archive integrity (e.g. checksum) before parsing\nif (!(await verifyChecksum(file))) throw new Error('corrupt archive');\nconst entries = await readUnixAr(file);","handlingStrategy":"validation","validationCode":"function hasLongNameTable(records: {name: string}[], longNames?: Uint8Array): boolean {\n  return !records.some(r => /^\\/\\d+$/.test(r.name)) || longNames !== undefined;\n}\nif (!hasLongNameTable(rawRecords, longNames)) throw new Error('long-name table corrupt or missing');","typeGuard":"function isPlausibleLongNameTable(t: Uint8Array | undefined): t is Uint8Array {\n  return t !== undefined && t.byteLength > 0;\n}","tryCatchPattern":"try {\n  const entries = await readUnixAr(source);\n} catch (err) {\n  if (err instanceof ArchiveError && /long name/.test(err.message)) {\n    throw new Error(`archive long-name table corrupt: ${err.message}`);\n  }\n  throw err;\n}","preventionTips":["Verify archive checksums from the publisher before parsing.","Reject archives whose '//'-table offsets point at empty lines during custom pre-scans.","Repack with GNU ar when interoperability issues appear.","Fuzz-test your archive pipeline with corrupted tables if inputs are untrusted."],"tags":["archive","corrupt-file","ar-format","parsing"],"backgroundTag":"corrupt-archive-long-name-table","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}