can1357/oh-my-pi · error · ArchiveError

Tar header field is too long

Error message

Tar header field is too long

What it means

When writing (serializing) a tar header, each field occupies a fixed number of bytes. writeField throws this ArchiveError if the value bytes exceed the field's fixed length. This guards against silently producing a corrupt archive with overlapping fields.

Source

Thrown at packages/utils/src/ar/tar.ts:606

	if (bytes.byteLength !== source.size) throw new ArchiveError("Invalid archive: truncated data");
	return readTarEntriesFromBuffer(bytes, options);
};

/** Detect a tar header, including legacy pre-ustar archives, by its checksum. */
export function sniffTar(bytes: Uint8Array): boolean {
	if (bytes.byteLength < BLOCK_SIZE) return false;
	if (isZeroBlock(bytes, 0)) return true;
	try {
		if (readTarString(bytes, NAME_OFFSET, NAME_LENGTH).length === 0) return false;
		const size = readTarSize(bytes, SIZE_OFFSET);
		return Number.isSafeInteger(paddedSize(size)) && checksumMatches(bytes, 0);
	} catch {
		return false;
	}
}

function writeField(target: Uint8Array, offset: number, length: number, value: Uint8Array): void {
	if (value.byteLength > length) throw new ArchiveError("Tar header field is too long");
	target.set(value, offset);
}

function writeOctal(target: Uint8Array, offset: number, length: number, value: number): void {
	if (!Number.isSafeInteger(value) || value < 0) throw new ArchiveError("Invalid tar numeric value");
	const digits = value.toString(8);
	if (digits.length > length - 1) throw new ArchiveError("Tar numeric value does not fit its header field");
	for (let index = offset; index < offset + length - 1 - digits.length; index++) target[index] = 0x30;
	for (let index = 0; index < digits.length; index++)
		target[offset + length - 1 - digits.length + index] = digits.charCodeAt(index);
	target[offset + length - 1] = 0;
}

function splitUstarPath(pathBytes: Uint8Array): readonly [Uint8Array, Uint8Array] | undefined {
	if (pathBytes.byteLength <= NAME_LENGTH) return [pathBytes, new Uint8Array(0)];
	for (let index = pathBytes.byteLength - 1; index > 0; index--) {
		if (pathBytes[index] !== 0x2f) continue;
		const prefix = pathBytes.subarray(0, index);

View on GitHub (pinned to 9690622007)

Solutions

  1. Let the writer emit PAX (pax_header) records for long paths instead of forcing plain ustar layout
  2. Shorten the member path before writing (strip or rebase the leading directories)
  3. If writing manually, split the path into prefix/name via splitUstarPath logic, or use GNU longname ('././@LongLink') extension
  4. Check the encoded byte length (Buffer.byteLength, not string length) against the field limit before writing

Example fix

// before: long path blows the 100-byte name field
addEntry({ path: `very/long/${repeat}/file.txt`, ... });
// after: rebase so the stored path fits
addEntry({ path: `very/long/${repeat}/file.txt`.replace(/^very\/long\//, ""), ... });
Defensive patterns

Strategy: validation

Validate before calling

const encoded = Buffer.byteLength(entryPath, "utf-8");
if (encoded > 100) {
  // ensure the writer supports pax/GNU long names, or shorten the path first
}

Prevention

When it happens

Trigger: Calling the tar writer with a name, link name, or other field whose UTF-8 encoding is longer than the fixed header slot (e.g. name > 100 bytes for plain ustar when no prefix split or pax record is applied).

Common situations: Very long file paths or deep directory trees written without ustar prefix splitting or GNU/pax long-name extension; long symlink targets; usernames/groupnames over 32 bytes.

Related errors


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