${title}
${description}${code}
${url} ${missingAssetsList(details.missingAssets)} ${repair}If this keeps happening, check logs/desktop.log and try
hermes desktop --force-build, then restart the app.
/** * Visible renderer-load error page. * * The white-screen failure modes this exists for: * * 1. TORN BUNDLE after an update (#95575): `hermes update` replaces the app * while its files are locked (antivirus, a still-running instance, an * interrupted Windows replace), leaving index.html and its hashed chunks * from DIFFERENT generations. The window loads, then dies on the first * lazy import ("Failed to fetch dynamically imported module") — a white * screen that no amount of restarting fixes. * 2. LOAD FAILURE: a missing/blocked index.html surfaces only as a bare * ERR_FILE_NOT_FOUND window (see #39484) with a log line nobody sees. * * Both used to leave the user staring at a blank window with the only * explanation in `logs/desktop.log`. This module renders the failure INTO * the window — error code, what is missing, how to repair — with a Reload * button, so the white screen becomes a diagnosable, actionable surface. * * Pure + injectable so it is testable without booting Electron: the page is * a self-contained data: URL (no network, no file access), so `loadURL` can * never itself fail on a torn install. */ export interface RendererLoadErrorDetails { /** Chromium error code, e.g. -6 (ERR_FILE_NOT_FOUND) or its name. */ errorCode?: number | string | undefined /** Human description of the failure, e.g. the renderer bundle is torn. */ errorDescription?: string /** The URL that failed to load, when known. */ url?: string /** Module files index.html declares but that are missing on disk. */ missingAssets?: string[] /** Repair command hint, e.g. `hermes desktop --force-build`. */ repairHint?: string /** * URL to navigate to when the user clicks Reload. On a data: page * `location.reload()` would just re-render the error page, so recovery * must target the real renderer URL. Omitted → the button reloads in * place (harmless: the caller's load-failure policy re-surfaces). */ reloadUrl?: string } /** * Escape a ``JSON.stringify`` result for embedding inside an inline * ``` ) } function escapeHtml(value: unknown): string { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') } function missingAssetsList(missingAssets?: string[]): string { const assets = (missingAssets ?? []).slice(0, 5) if (assets.length === 0) { return '' } const items = assets.map(asset => `
${escapeHtml(asset)}The renderer bundle is missing ${missingAssets!.length} module file(s) ` + `(first ${assets.length} shown) — the last update replaced the app while ` + `its files were locked.
${escapeHtml(details.url)}
Repair with: hermes desktop --force-build
${description}${code}
${url} ${missingAssetsList(details.missingAssets)} ${repair}If this keeps happening, check logs/desktop.log and try
hermes desktop --force-build, then restart the app.