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
- Convert absolute paths to archive-relative before encoding: strip the common root directory
- Sanitize each segment: drop empty parts, '.', and '..' entries
- 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
- Always convert paths with path.relative(root, file) before packing
- Strip drive letters and leading separators; join segments with '/'
- Reject or normalize '..' segments at input boundaries
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
- Absolute paths are not allowed in omp:// URLs
- Codex Security bundle locations must be repository-relative:
- message (from validateRelativePath)
- ASAR members must be [path, Uint8Array] pairs
- Failed to encode ASAR archive: ${describeError(error)}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4378aad65fd95af9.
Report an issue: GitHub.