can1357/oh-my-pi · error · ArchiveError

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

Error message

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

What it means

Thrown when a tar hard link's target resolves to a path that is not a directory and has no readable file member behind it — either the target member exists but has no readable storage ('unreadable member', e.g. a symlink kept as a raw link) or no member exists at all ('missing member'). Hard links require a concrete file target.

Source

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

				);
			}
			entry.storage = { type: "link", targetPath: pending.targetPath, resolveTarget: false };
			continue;
		}
		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);

View on GitHub (pinned to 9690622007)

Solutions

  1. Check `tar tvf` for the target path named in the message and confirm the file member is present.
  2. Re-create the archive ensuring hard-link targets are archived as regular files (GNU tar archives targets in full when needed).
  3. If you control archive generation, emit hard links after their target member, or replace hard links with regular file copies.

Example fix

// before (creating archive): hard link with target omitted
tar -cf out.tar link-only-dir/
// after: include the target file or dereference links
tar -cf out.tar -h dir/
Defensive patterns

Strategy: validation

Validate before calling

// Verify with system tar before indexing that all hardlink targets exist
// $ tar -tvf archive.tar | review typeflag-1 entries and their targets

Try / catch

try {
  await readTar(bytes, opts);
} catch (e) {
  if (e instanceof ArchiveError && e.message.includes("targets unreadable member") ||
      e instanceof ArchiveError && e.message.includes("targets missing member")) {
    throw new Error("Archive hard link points at a missing/unreadable member; re-create archive");
  }
  throw e;
}

Prevention

When it happens

Trigger: readTar on an archive with a typeflag-'1' entry whose linkname points at a path absent from the index, or at a member whose storage cannot be shared (e.g. a dangling symlink target).

Common situations: Archives extracted/re-packed lossily so the hard-link target file was dropped; tools emitting hard links before their target member; hand-crafted archives.

Related errors


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