can1357/oh-my-pi · error · ArchiveError

Invalid ASAR member path '${formatArchivePathForError(rawPat

Error message

Invalid ASAR member path '${formatArchivePathForError(rawPath)}'

What it means

Thrown by writerPath (used by encodeAsar via memberPath) when a member path is not a valid, safe relative ASAR path: empty, absolute (leading '/' or Windows drive 'C:/'), containing NUL, or having empty/'.'/'..' segments after normalization. The library refuses to write entries that would escape the archive root or be unresolvable.

Source

Thrown at packages/utils/src/ar/asar.ts:399

		headerSize >= ASAR_INNER_PREFIX_SIZE &&
		headerSize === innerPayload + 4 &&
		innerPayload === 4 + alignAsarPayload(jsonSize) &&
		jsonSize > 0 &&
		bytes[ASAR_JSON_OFFSET] === 0x7b
	);
}

function writerPath(rawPath: string): string {
	const portable = rawPath.replace(/\\/g, "/");
	const normalized = normalizeArchiveEntryPath(portable);
	if (
		normalized === undefined ||
		portable.startsWith("/") ||
		/^[A-Za-z]:\//.test(portable) ||
		portable.includes("\0") ||
		portable.split("/").some(part => !part || part === "." || part === "..")
	) {
		throw new ArchiveError(`Invalid ASAR member path '${formatArchivePathForError(rawPath)}'`);
	}
	return normalized;
}

/** Encode file members in Electron's Pickle-framed ASAR layout. */
export async function encodeAsar(members: Iterable<readonly [string, Uint8Array]>): Promise<Uint8Array> {
	try {
		const root: AsarDirectoryNode = { files: Object.create(null) as Record<string, AsarNode> };
		const payloads: Uint8Array[] = [];
		let payloadSize = 0;
		for (const member of members) {
			if (
				!Array.isArray(member) ||
				member.length !== 2 ||
				typeof member[0] !== "string" ||
				!(member[1] instanceof Uint8Array)
			) {
				throw new ArchiveError("ASAR members must be [path, Uint8Array] pairs");

View on GitHub (pinned to 9690622007)

Solutions

  1. Convert absolute paths to archive-relative before encoding: strip the common root directory
  2. Sanitize each segment: drop empty parts, '.', and '..' entries
  3. Use path.relative(rootDir, filePath) (not join) and normalize backslashes to '/'

Example fix

// before
members.push([path.join(rootDir, file), bytes]);
// after
members.push([path.relative(rootDir, file).split(path.sep).join("/"), bytes]);
Defensive patterns

Strategy: validation

Validate before calling

function toAsarPath(p) {
  const parts = p.replace(/\\/g, "/").split("/").filter(s => s && s !== "." && s !== "..");
  if (!parts.length || /^[A-Za-z]:\//.test(p)) throw new Error(`not archive-relative: ${p}`);
  return parts.join("/");
}

Type guard

const isSafeAsarPath = (p) => typeof p === "string" && !p.startsWith("/") && !/^[A-Za-z]:\//.test(p) && !p.includes("\0") && p.split("/").every(s => s && s !== "." && s !== "..");

Try / catch

try { await encodeAsar(members); } catch (e) { if (e instanceof ArchiveError && e.message.startsWith("Invalid ASAR member path")) { console.error(`sanitize paths before packing: ${e.message}`); } throw e; }

Prevention

When it happens

Trigger: encodeAsar called with paths like '/etc/passwd', '../secret', 'C:/foo', 'a//b', './x', or non-string paths (the shape check precedes this).

Common situations: Building the member list with path.join(dir, name) where dir is absolute; unzipping a tar/zip straight into encodeAsar where entries contain '..'; Windows path separators handled partially (backslashes are converted, but drive letters are rejected).

Related errors


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