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

  1. Regenerate the archive with standard cpio tooling (GNU cpio, bsdtar) which always NUL-terminates names
  2. Fix your writer: write name + '\0' and set nameSize = nameBytes.length + 1 so the terminator lies inside the declared field
  3. Compare a known-good archive's hex dump against yours at the failing entry to spot the missing NUL or offset drift
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/e4bfbc665fe8e652. Report an issue: GitHub.