{"record":{"id":"ffa9c88dd2cb30b2","repo":"can1357/oh-my-pi","slug":"invalid-ar-archive-bsd-extended-name-length","errorCode":null,"errorMessage":"Invalid ar archive BSD extended name length","messagePattern":"Invalid ar archive BSD extended name length","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/unix-ar.ts","lineNumber":98,"sourceCode":"\tbsdNameLength?: number;\n} {\n\tif (\n\t\theader.byteLength !== HEADER_SIZE ||\n\t\theader[HEADER_TRAILER_OFFSET] !== 0x60 ||\n\t\theader[HEADER_TRAILER_OFFSET + 1] !== 0x0a\n\t) {\n\t\tthrow new ArchiveError(\"Invalid ar archive member header\");\n\t}\n\tconst rawName = decodeAsciiField(header, 0, NAME_SIZE);\n\tconst mtimeSeconds = parseOptionalNumber(decodeAsciiField(header, 16, 12), 10, \"modification time\");\n\tparseOptionalNumber(decodeAsciiField(header, 28, 6), 10, \"user id\");\n\tparseOptionalNumber(decodeAsciiField(header, 34, 6), 10, \"group id\");\n\tconst mode = parseOptionalNumber(decodeAsciiField(header, 40, 8), 8, \"mode\");\n\tconst physicalSize = parseRequiredSize(decodeAsciiField(header, 48, 10));\n\tlet bsdNameLength: number | undefined;\n\tif (rawName.startsWith(\"#1/\")) {\n\t\tconst encodedLength = rawName.slice(3);\n\t\tif (!/^\\d+$/.test(encodedLength)) throw new ArchiveError(\"Invalid ar archive BSD extended name length\");\n\t\tbsdNameLength = Number.parseInt(encodedLength, 10);\n\t\tif (!Number.isSafeInteger(bsdNameLength) || bsdNameLength <= 0 || bsdNameLength > physicalSize) {\n\t\t\tthrow new ArchiveError(\"Invalid ar archive BSD extended name length\");\n\t\t}\n\t}\n\treturn { rawName, physicalSize, mtimeSeconds, mode, bsdNameLength };\n}\n\nfunction decodeName(bytes: Uint8Array, limits: ArchiveLimits): string {\n\tassertArchivePathBytes(bytes.byteLength, \"member path\", limits.maxPathBytes);\n\treturn UTF8_DECODER.decode(bytes);\n}\n\nfunction decodeBsdName(bytes: Uint8Array, limits: ArchiveLimits): { name: string; byteLength: number } {\n\tconst nul = bytes.indexOf(0);\n\tconst nameBytes = nul >= 0 ? bytes.subarray(0, nul) : bytes;\n\tif (nameBytes.byteLength === 0) throw new ArchiveError(\"Invalid ar archive empty BSD extended name\");\n\treturn { name: decodeName(nameBytes, limits), byteLength: nameBytes.byteLength };","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/unix-ar.ts#L80-L116","documentation":"For BSD-style extended names ('#1/<len>'), parseHeader extracts the length digits from the raw name field and validates them: all digits, a safe integer, strictly positive, and not larger than the member's physical size (the name is stored at the start of the member data, so it can't exceed it). This first throw fires when the text after '#1/' is not purely digits.","triggerScenarios":"Parsing an archive whose member rawName starts with '#1/' followed by non-digit characters — e.g. '#1/abc' or '#1/' with trailing content, or a name coincidentally beginning with '#1/' in a non-BSD archive.","commonSituations":"Archives from BSD ar (macOS) parsed where the GNU convention was expected; custom writers emitting '#1/' prefixes incorrectly; filenames that literally start with '#1/' written without BSD long-name encoding.","solutions":["Confirm the producer is BSD ar and the '#1/' encoding is intended; regenerate with consistent tooling (llvm-ar / GNU ar) for cross-platform compatibility","Hex-dump the name field (bytes 0-15) and check what follows '#1/'","Rename files starting with '#1/' in the producer or escape them via the intended encoding","Ensure the writer stores the 4-byte length in the member data as BSD ar does for #1/ names"],"exampleFix":"// before: malformed BSD name field\nrawName = '#1/ 12'      // digits check fails on the space\n// after: correct BSD form\nrawName = '#1/12       ' // decoded length digits '12'","handlingStrategy":"validation","validationCode":"function bsdNamePrefixIsValid(rawName: string): boolean {\n  if (!rawName.startsWith('#1/')) return true; // not a BSD long name\n  return /^\\d+$/.test(rawName.slice(3));\n}\nif (!bsdNamePrefixIsValid(rawNameField)) throw new Error('malformed #1/ BSD name');","typeGuard":"function isBsdLongNameRef(rawName: string): boolean {\n  return /^#1\\/\\d+$/.test(rawName);\n}","tryCatchPattern":"try {\n  const names = await archive.list();\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('BSD extended name length')) {\n    // archive mixes BSD naming unexpectedly — inspect with ar t / llvm-ar t\n  } else throw err;\n}","preventionTips":["Use one ar variant (GNU or BSD) consistently for producing archives","Don't create files whose names begin with '#1/' unless BSD encoding is intended","Validate name fields in your writer before serialization","Test cross-platform archives (macOS BSD ar vs GNU ar) in CI"],"tags":["archive","parsing","ar-format","bsd","naming"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}