can1357/oh-my-pi · error · ArchiveError

Invalid empty tar member path

Error message

Invalid empty tar member path

What it means

After normalization the member path encoded to zero bytes, meaning the tar would contain a header with an empty name. Tar format requires a non-empty member name, so the encoder throws.

Source

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

			mtime,
			directory ? 0o755 : 0o644,
			directory ? 0x35 : 0x30,
		),
	);
	if (!directory) appendPayload(parts, payload);
}

/** Encode files as a deterministic ustar archive, using PAX records for overflow paths. */
export async function encodeTar(members: Iterable<readonly [string, Uint8Array]>): Promise<Uint8Array> {
	const parts: Uint8Array[] = [];
	const kinds = new Map<string, "directory" | "file">();
	let sequence = 0;
	for (const [rawPath, bytes] of members) {
		const directory = rawPath.endsWith("/") || rawPath.endsWith("\\");
		const normalized = normalizeArchiveEntryPath(rawPath);
		if (!normalized) throw new ArchiveError(`Invalid tar member path '${formatArchivePathForError(rawPath)}'`);
		const pathBytes = TEXT_ENCODER.encode(normalized);
		if (pathBytes.byteLength === 0) throw new ArchiveError("Invalid empty tar member path");
		if (directory && bytes.byteLength !== 0) {
			throw new ArchiveError(`Tar directory '${formatArchivePathForError(normalized)}' cannot contain file data`);
		}
		const segments = normalized.split("/");
		for (let index = 1; index < segments.length; index++) {
			const parent = segments.slice(0, index).join("/");
			const kind = kinds.get(parent);
			if (kind === "file")
				throw new ArchiveError(`Tar member '${formatArchivePathForError(parent)}' is not a directory`);
			if (kind === "directory") continue;
			kinds.set(parent, "directory");
			appendTarEntry(parts, parent, new Uint8Array(0), true, sequence++);
		}
		const existing = kinds.get(normalized);
		if (existing) {
			if (directory && existing === "directory") continue;
			throw new ArchiveError(`Duplicate tar member path '${formatArchivePathForError(normalized)}'`);
		}

View on GitHub (pinned to 9690622007)

Solutions

  1. Filter out empty-string keys from the members map before calling createTar
  2. Default the member name to a fallback like 'file' when the source path is empty

Example fix

// before
members.set(rawName ?? "", bytes);
// after
if (rawName) members.set(rawName, bytes);
Defensive patterns

Strategy: validation

Validate before calling

for (const key of members.keys()) {
	if (key.trim().length === 0) throw new Error(`Empty member path`);
}

Type guard

const hasNonEmptyPaths = (m: Map<string, Uint8Array>): boolean =>
	[...m.keys()].every((k) => k.length > 0);

Try / catch

try {
	return await createTar(members);
} catch (err) {
	if (err instanceof ArchiveError && err.message === "Invalid empty tar member path") {
		// drop or rename empty-keyed members
	}
	throw err;
}

Prevention

When it happens

Trigger: Passing '' as a member key that survives normalization (e.g. an empty-string Map entry) — distinct from 3652, which fires when normalization returns nothing at all; the raw path normalized to something whose UTF-8 encoding is empty.

Common situations: Building a members map programmatically where a variable holding the path was empty; iterating a directory listing that included an empty relative path.

Related errors


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