can1357/oh-my-pi · error · ArchiveError
Invalid CPIO archive: member name is not NUL-terminated
Error message
Invalid CPIO archive: member name is not NUL-terminated
What it means
After bounds-checking the name field, readCpioEntriesFromBuffer asserts the last byte of the declared name field is NUL (bytes[nameEnd-1] === 0) and throws ArchiveError if not. CPIO names are NUL-terminated strings whose size includes the terminator; a missing NUL means the writer under-counted nameSize, wrote the name without a terminator, or the archive bytes are shifted/corrupt.
Source
Thrown at packages/utils/src/ar/cpio.ts:239
let metadataSize = 0;
let foundTrailer = false;
while (offset < bytes.byteLength) {
const header = parseHeader(bytes, offset);
if (header.mode > 0xffff) throw new ArchiveError("Invalid CPIO archive: mode exceeds 16 bits");
if (header.nameSize < 1) throw new ArchiveError("Invalid CPIO archive: name size must include a NUL terminator");
assertArchivePathBytes(header.nameSize - 1, "member path", options.limits.maxPathBytes);
assertArchiveMemberSize(header.fileSize, "(CPIO entry)", options.limits);
const nameStart = offset + header.headerSize;
const nameEnd = nameStart + header.nameSize;
const dataOffset = align(nameEnd, header.alignment);
const dataEnd = dataOffset + header.fileSize;
const nextOffset = align(dataEnd, header.alignment);
requireRange(bytes, nameStart, nameEnd, "member name");
requireRange(bytes, dataOffset, dataEnd, "member data");
requireRange(bytes, dataEnd, nextOffset, "member padding");
if (bytes[nameEnd - 1] !== 0) throw new ArchiveError("Invalid CPIO archive: member name is not NUL-terminated");
for (let index = nameStart; index < nameEnd - 1; index++) {
if (bytes[index] === 0) throw new ArchiveError("Invalid CPIO archive: member name contains an embedded NUL");
}
validateZeroPadding(bytes, nameEnd, dataOffset, "name");
validateZeroPadding(bytes, dataEnd, nextOffset, "data");
metadataSize += dataOffset - offset;
assertIndexSize(metadataSize, options.limits, "CPIO index");
const rawName = decodeUtf8(bytes.subarray(nameStart, nameEnd - 1));
if (rawName === TRAILER_NAME) {
if (header.fileSize !== 0) throw new ArchiveError("Invalid CPIO archive: TRAILER!!! has non-empty data");
foundTrailer = true;
offset = nextOffset;
break;
}
const fileType = header.mode & FILE_TYPE_MASK;
if ((fileType === FILE_TYPE_DIRECTORY || fileType === 0o010000) && header.fileSize !== 0) {View on GitHub (pinned to 9690622007)
Solutions
- Regenerate the archive with standard cpio tooling (GNU cpio, bsdtar) which always NUL-terminates names
- Fix your writer: write name + '\0' and set nameSize = nameBytes.length + 1 so the terminator lies inside the declared field
- Compare a known-good archive's hex dump against yours at the failing entry to spot the missing NUL or offset drift
- Validate generated archives in CI by round-tripping through this reader before shipping
Example fix
// before: terminator written outside declared field buf.write(name, nameOffset); buf.writeUInt8(0, nameOffset + name.length); header.nameSize = name.length; // after: terminator inside the field header.nameSize = Buffer.byteLength(name) + 1; buf.write(name + '\0', nameOffset);
Defensive patterns
Strategy: try-catch
Validate before calling
// validate your writer output before shipping const out = buildCpio(entries); readCpioEntriesFromBuffer(out, options); // throws early on missing NUL terminators
Try / catch
try {
const entries = await readCpio(source, options);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('not NUL-terminated')) {
// regenerate/repack the archive; check the writer's nameSize accounting
} else throw err;
} Prevention
- Write member names with a trailing NUL and set nameSize = byteLength(name) + 1
- Round-trip custom archives through this reader before distribution
- Avoid post-processing archive bytes in place
- Use GNU cpio/bsdtar as a reference implementation for header layout
When it happens
Trigger: Parsing an archive where the name field's final byte is non-NUL — a writer that set nameSize = name.length (excluding NUL) while still writing the name, a writer that omitted the NUL entirely, or offset drift so the checked byte lands on padding or data.
Common situations: Hand-rolled or minimal archive writers forgetting the terminator; archives post-processed/patched incorrectly; corruption in the name region; mixing formats where alignment assumptions moved the name boundary.
Related errors
- Invalid CPIO archive: ${field} is not a valid base-${radix}
- Invalid tar octal value: ${value}
- Invalid ARJ ${field}: missing terminator
- Invalid ARJ header signature
- Invalid ARJ basic header size
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e4bfbc665fe8e652.
Report an issue: GitHub.