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
- Find the offending hard link via `tar tvf` and remove it or convert it to a symlink.
- Re-create the archive excluding directory hard links (`tar --exclude'...'` or fix the source tree).
- 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
- Never hard-link directories when building archives (most tars forbid it already).
- Convert directory hard links to symlinks if a reference is genuinely needed.
- Reject such archives in security-sensitive pipelines.
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
- Archive hard link '${formatArchivePathForError(entry.path)}'
- Archive hard link '${formatArchivePathForError(entry.path)}'
- {}: hard link not allowed for directory
- Invalid tar octal value: ${value}
- Truncated embedded addon archive entry: ${filename}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/44aaae5a0e6788f8.
Report an issue: GitHub.