can1357/oh-my-pi · error · ArchiveError

Invalid LZH symbolic link '${header.path}'

Error message

Invalid LZH symbolic link '${header.path}'

What it means

A directory entry (-lhd-) whose mode indicates a symlink (S_IFLNK, 0xa000) must encode its link target after a '|' separator in the path ('linkpath|target'). If the path has no '|' or the '|' would start the link name (index < 1), the entry is malformed and cannot be interpreted as a symbolic link.

Source

Thrown at packages/utils/src/ar/lzh.ts:629

	}
	if (bytes.byteLength !== source.size) throw new ArchiveError("Invalid LZH archive: truncated data");
	if (!sniffLzh(bytes)) throw new ArchiveError("Invalid LZH archive header");
	const entries: ArchiveIndexEntry[] = [];
	let offset = 0;
	let parsedCount = 0;
	let metadataSize = 0;
	while (offset < bytes.byteLength && bytes[offset] !== 0) {
		const header = parseLzhHeader(bytes, offset, options);
		metadataSize += header.dataStart - offset;
		assertIndexSize(metadataSize, options.limits, "index");
		assertEntryCount(++parsedCount, options.limits);
		if (header.nextOffset <= offset) throw new ArchiveError("Invalid LZH archive: header did not advance");
		offset = header.nextOffset;
		if (!header.path) continue;
		const isDirectory = header.method === "-lhd-";
		if (isDirectory && header.mode !== undefined && (header.mode & 0xf000) === 0xa000) {
			const separator = header.path.indexOf("|");
			if (separator < 1) throw new ArchiveError(`Invalid LZH symbolic link '${header.path}'`);
			const path = normalizeArchiveEntryPath(header.path.slice(0, separator));
			const targetPath = normalizeArchiveEntryPath(header.path.slice(separator + 1));
			if (!path || !targetPath) continue;
			entries.push({
				path,
				isDirectory: false,
				size: 0,
				mtimeMs: header.mtimeMs,
				mode: header.mode,
				storage: { type: "link", targetPath, resolveTarget: false },
			});
			continue;
		}
		entries.push({
			path: header.path,
			isDirectory,
			size: isDirectory ? 0 : header.size,
			mtimeMs: header.mtimeMs,

View on GitHub (pinned to 9690622007)

Solutions

  1. Inspect the offending member with an external LHA tool to see its raw path and mode.
  2. Recreate the archive with a tool that encodes symlinks as 'linkpath|target' (e.g. modern lha/lhasa on Unix).
  3. If the entry should be a plain directory, correct its mode (remove the S_IFLNK bits) rather than -lhd- with 0xa000.
  4. Catch ArchiveError and skip/flag the archive if symlink metadata is not needed.

Example fix

// before: creating a symlink member with the wrong path encoding
path = "mylink"; // missing target
// after: encode as link|target
path = "mylink|/etc/hostname";
Defensive patterns

Strategy: validation

Validate before calling

// pre-screen symlink-style directory entries before reading
// (readLzh throws for -lhd- + S_IFLNK without 'link|target' encoding)
// prefer archives created by tools known to encode symlinks correctly
if (!createdByModernLhaTool(archive)) {
  await validateSymlinkEntriesExternally(path); // e.g. `lha l` output check
}

Try / catch

try {
  const entries = await readLzh(source, options);
} catch (err) {
  if (err instanceof ArchiveError && err.message.startsWith("Invalid LZH symbolic link")) {
    throw new Error("archive stores symlinks in a non-standard way — repack with lha/lhasa");
  }
  throw err;
}

Prevention

When it happens

Trigger: readLzh() on an LZH archive containing a -lhd- entry with symlink mode bits but a path missing the 'link|target' separator, e.g. 'foo|' or just 'foo'.

Common situations: Archives produced by tools that store symlinks non-standardly; corrupted path fields; hand-built archives where the symlink encoding convention was not followed.

Related errors


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