can1357/oh-my-pi · error · ArchiveError

Archive hard link '${formatArchivePathForError(entry.path)}'

Error message

Archive hard link '${formatArchivePathForError(entry.path)}' targets directory '${formatArchivePathForError(pending.targetPath)}'

What it means

Thrown when a tar hard link's target resolves to a directory. POSIX hard links cannot reference directories, so the library refuses to index such an entry rather than fabricating a directory.

Source

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

		const target = entries.get(targetPath);
		if (target?.storage && !target.isDirectory && !unresolved.has(target)) {
			entry.size = target.size;
			entry.storage = target.storage;
			continue;
		}
		const targetIsDirectory = targetPath === "" || target?.isDirectory === true || directoryPrefixes.has(targetPath);
		if (!targetIsDirectory) {
			if (pending.kind === "symlink") {
				entry.storage = { type: "link", targetPath: pending.targetPath, resolveTarget: false };
				continue;
			}
			const reason = target ? "unreadable member" : "missing member";
			throw new ArchiveError(
				`Archive hard link '${formatArchivePathForError(entry.path)}' targets ${reason} '${formatArchivePathForError(pending.targetPath)}'`,
			);
		}
		if (pending.kind === "hard link") {
			throw new ArchiveError(
				`Archive hard link '${formatArchivePathForError(entry.path)}' targets directory '${formatArchivePathForError(pending.targetPath)}'`,
			);
		}
		entry.isDirectory = true;
		entry.storage = { type: "link", targetPath: pending.targetPath, resolveTarget: false };
	}
	if (unresolved.size > 0) throw new ArchiveError("Archive contains cyclic or unsupported links");
}

/** Index an already-decompressed tar buffer with bounded GNU, ustar, and PAX handling. */
export function readTarEntriesFromBuffer(buffer: Uint8Array, options: FormatReadOptions): ArchiveIndexEntry[] {
	const { limits } = options;
	assertInMemorySize(buffer.byteLength, limits);
	const entries = new Map<string, ArchiveIndexEntry>();
	const pendingLinks = new Map<ArchiveIndexEntry, PendingTarLink>();
	const addEntry = (entry: ArchiveIndexEntry, pendingLink?: PendingTarLink): void => {
		const existing = entries.get(entry.path);
		const indexed = upsertArchiveEntry(entries, entry);

View on GitHub (pinned to 9690622007)

Solutions

  1. Find the offending hard link via `tar tvf` and remove it or convert it to a symlink.
  2. Re-create the archive excluding directory hard links (`tar --exclude'...'` or fix the source tree).
  3. Catch ArchiveError and reject the archive if it comes from an untrusted source.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await readTar(bytes, opts);
} catch (e) {
  if (e instanceof ArchiveError && e.message.includes("targets directory")) {
    throw new Error("Archive contains an illegal directory hard link");
  }
  throw e;
}

Prevention

When it happens

Trigger: readTar on an archive with a typeflag-'1' entry whose linkname points at a directory entry (typeflag '5') or a path that is a directory prefix of other members.

Common situations: Corrupt or hand-crafted archives; archives migrated between systems where directory hard links were (incorrectly) serialized; old backup tools that recorded directory hard links.

Related errors


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