{"record":{"id":"2fdc4412428d92be","repo":"can1357/oh-my-pi","slug":"invalid-ar-archive-empty-bsd-extended-name","errorCode":null,"errorMessage":"Invalid ar archive empty BSD extended name","messagePattern":"Invalid ar archive empty BSD extended name","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/unix-ar.ts","lineNumber":115,"sourceCode":"\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 };\n}\n\nfunction shortName(rawName: string): string {\n\tif (rawName === \"/\" || rawName === \"//\" || rawName === \"/SYM64/\") return rawName;\n\treturn rawName.endsWith(\"/\") ? rawName.slice(0, -1) : rawName;\n}\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}'`);","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/unix-ar.ts#L97-L133","documentation":"decodeBsdName extracts the BSD extended name from the member data up to the first NUL byte and throws if the resulting name is empty (byte length 0) — i.e. the name region starts with a NUL or has no bytes. Zero-length names are rejected earlier via the '#1/0' range check, so this fires when stored name data is empty despite a claimed positive length.","triggerScenarios":"Parsing a BSD '#1/<len>' member whose data begins with 0x00, or whose length refers only to padding bytes, reached through the header decode path during listing/extraction.","commonSituations":"Custom BSD-style writers that declare a length but forget to write the name bytes; archives where UTF-8 name bytes were stripped; corruption zeroing the head of the member data.","solutions":["Verify the producer writes the name bytes (length includes name plus NUL padding to even size) immediately at the start of member data","Hex-dump the first bytes of the member data; the filename should appear right after the header","Regenerate with BSD ar / llvm-ar","Use plain ASCII member names if the producer mishandles multibyte names"],"exampleFix":"// before: length declared, name bytes missing\nmemberData = new Uint8Array(4); // padding only\n// after: name + NUL, padded to even\nconst raw = Buffer.concat([Buffer.from(name), Buffer.alloc(1)]);\nconst data = raw.byteLength % 2 ? Buffer.concat([raw, Buffer.alloc(1)]) : raw;","handlingStrategy":"validation","validationCode":"function bsdNameDataPresent(memberData: Uint8Array): boolean {\n  const nul = memberData.indexOf(0);\n  const nameBytes = nul >= 0 ? memberData.subarray(0, nul) : memberData;\n  return nameBytes.byteLength > 0;\n}\n// check on raw member bytes before handing to the parser","typeGuard":"function hasNonEmptyName(data: Uint8Array): boolean {\n  return data.byteLength > 0 && data[0] !== 0;\n}","tryCatchPattern":"try {\n  await archive.extract(dest);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('empty BSD extended name')) {\n    // member data starts with NUL — regenerate archive with BSD ar\n  } else throw err;\n}","preventionTips":["Writers must emit name bytes immediately after the header for #1/ members","NUL-terminate and pad the name to even byte length","Round-trip parse archives right after writing them","Avoid tools that rewrite member payloads without preserving name data"],"tags":["archive","parsing","ar-format","bsd","empty-value"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}