can1357/oh-my-pi · error · ArchiveError

Invalid CPIO archive: symlink '${recordPath}' has an invalid

Error message

Invalid CPIO archive: symlink '${recordPath}' has an invalid UTF-8 target

What it means

When a symlink entry is indexed, makeLinkTarget decodes the link-target bytes and throws ArchiveError naming the offending record path if the bytes are not valid UTF-8 (fatal decoding fails) or contain an embedded NUL. The library requires symlink targets to be well-formed UTF-8 text without NULs so it can represent them as string targetPath values.

Source

Thrown at packages/utils/src/ar/cpio.ts:206

function decodeUtf8(bytes: Uint8Array): string | undefined {
	try {
		return UTF8_FATAL_DECODER.decode(bytes);
	} catch {
		return undefined;
	}
}

function validateZeroPadding(bytes: Uint8Array, start: number, end: number, what: string): void {
	for (let offset = start; offset < end; offset++) {
		if (bytes[offset] !== 0) throw new ArchiveError(`Invalid CPIO archive: non-zero ${what} padding`);
	}
}

function makeLinkTarget(recordPath: string, targetBytes: Uint8Array, maxPathBytes: number): LinkTarget {
	assertArchivePathBytes(targetBytes.byteLength, "link target", maxPathBytes);
	const rawTarget = decodeUtf8(targetBytes);
	if (rawTarget === undefined || rawTarget.includes("\0")) {
		throw new ArchiveError(`Invalid CPIO archive: symlink '${recordPath}' has an invalid UTF-8 target`);
	}
	const portableTarget = rawTarget.replace(/\\/g, "/");
	if (path.posix.isAbsolute(portableTarget)) return { path: portableTarget, resolveTarget: false };
	const normalized = normalizeArchiveLookupPath(path.posix.join(path.posix.dirname(recordPath), portableTarget));
	return normalized === undefined
		? { path: portableTarget, resolveTarget: false }
		: { path: normalized, resolveTarget: true };
}

/** Parse an already-materialized CPIO stream for direct and RPM-composed readers. */
export function readCpioEntriesFromBuffer(bytes: Uint8Array, options: FormatReadOptions): ArchiveIndexEntry[] {
	assertInMemorySize(bytes.byteLength, options.limits);
	const records: ParsedRecord[] = [];
	let offset = 0;
	let metadataSize = 0;
	let foundTrailer = false;

	while (offset < bytes.byteLength) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Recreate the archive so symlink targets are UTF-8 encoded (set a UTF-8 locale or convert filenames)
  2. If the archive is legitimately non-UTF-8, transcode it before parsing (repack with bsdtar or GNU cpio after fixing encoding)
  3. Inspect the named symlink's data bytes to confirm whether it is corruption or encoding mismatch
  4. Exclude or pre-process entries with binary targets if your pipeline controls archive creation

Example fix

// before: writing raw platform bytes as target
writer.addSymlink('link', Buffer.from(targetName, 'latin1'));
// after: enforce UTF-8
writer.addSymlink('link', Buffer.from(targetName, 'utf8'));
Defensive patterns

Strategy: try-catch

Validate before calling

// when creating archives, guarantee UTF-8 symlink targets:
if (!Buffer.from(target, 'utf8').equals(Buffer.from(target))) throw new Error('symlink target must be UTF-8');

Try / catch

try {
  const entries = await readCpio(source, options);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('invalid UTF-8 target')) {
    // transcode the archive (repack with UTF-8 filenames) or skip affected symlinks
  } else throw err;
}

Prevention

When it happens

Trigger: readCpio/readRpmArchive/entries parsing a CPIO containing a symlink (mode 0o120xxx) whose data payload (the target path) is non-UTF-8 (e.g. Latin-1 encoded filename) or NUL-contaminated, e.g. the writer included the terminating NUL inside the target content while fileSize also counted it twice.

Common situations: Archives created on systems with non-UTF-8 locales/filesystems; old tooling writing raw filesystem bytes for targets; corrupt or truncated entries where a NUL sneaks into target bytes.

Understand the failure class

Related errors


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