NousResearch/hermes-agent · error
Desktop preview browser bridge is unavailable
Error message
Desktop preview browser bridge is unavailable
What it means
Thrown by openPreviewTargetInBrowser() in apps/desktop/src/lib/local-preview.ts:151 when `window.hermesDesktop.openPreviewInBrowser` is missing. Opening a preview target in the user's real browser is an Electron-main-process capability (shell.openExternal / staged file opening) injected via preload; without it — older shell, web context, or stripped preload — the function refuses up front rather than half-executing.
Source
Thrown at apps/desktop/src/lib/local-preview.ts:151
for (const attribute of Array.from(element.attributes)) {
if (attribute.localName === 'href' || attribute.localName === 'ping') {
element.removeAttributeNode(attribute)
}
}
})
const policy = document.createElement('meta')
policy.httpEquiv = 'Content-Security-Policy'
policy.content = csp
document.head.prepend(policy)
return `<!doctype html>${document.documentElement.outerHTML}`
}
export async function openPreviewTargetInBrowser(target: PreviewTarget): Promise<void> {
const bridge = window.hermesDesktop
if (!bridge?.openPreviewInBrowser) {
throw new Error('Desktop preview browser bridge is unavailable')
}
const dataUrl = target.dataUrl && validatedRemoteHtmlDataUrl(target.dataUrl)
if (!dataUrl) {
if (target.transient) {
throw new Error('Remote HTML preview could not be loaded')
}
await bridge.openPreviewInBrowser(target.url)
return
}
if (!bridge.saveImageBuffer) {
throw new Error('Desktop preview buffer bridge is unavailable')
}
View on GitHub (pinned to c896c09c42)
Solutions
- Gate the 'open in browser' affordance: hide it when `!window.hermesDesktop?.openPreviewInBrowser`.
- Update and restart the desktop app so the preload exposes the capability.
- In tests, stub openPreviewInBrowser (and saveImageBuffer for data-url targets) on the bridge.
- Fall back to rendering the preview in-app (the embedded preview path) when the browser bridge is absent.
Example fix
// before
await openPreviewTargetInBrowser(target)
// after
if (!window.hermesDesktop?.openPreviewInBrowser) {
showPreviewInline(target) // in-app fallback instead of hard failure
return
}
await openPreviewTargetInBrowser(target) Defensive patterns
Strategy: type-guard
Validate before calling
function canOpenPreviewInBrowser(): boolean {
return typeof window.hermesDesktop?.openPreviewInBrowser === 'function'
} Type guard
function hasPreviewBridge(w: Window): w is Window & { hermesDesktop: { openPreviewInBrowser: (url: string) => Promise<void>; saveImageBuffer?: (b: Uint8Array, ext: string) => Promise<string | null> } } {
return typeof (w as any).hermesDesktop?.openPreviewInBrowser === 'function'
} Try / catch
if (!hasPreviewBridge(window)) { showPreviewInline(target); return }
try { await openPreviewTargetInBrowser(target) } catch (e) { if (e instanceof Error && e.message.includes('bridge is unavailable')) showPreviewInline(target) else throw e } Prevention
- Render 'open in browser' buttons only when the capability exists
- Update and restart the desktop shell together with the renderer
- Provide an in-app preview fallback so absence of the bridge degrades gracefully
- Stub both openPreviewInBrowser and saveImageBuffer in tests when exercising data-url targets
When it happens
Trigger: Clicking 'open in browser' on a preview card in a web build or non-Electron context; running on an older desktop shell that predates the openPreviewInBrowser IPC; a bridge stub in tests lacking the method.
Common situations: Version skew between a newly updated renderer and an older installed shell (restart required); preview UI mounted outside the desktop app; feature-gated preloads.
Related errors
- Hermes desktop bridge unavailable
- Hermes Desktop bridge is unavailable
- Rename is not available
- Delete is not available
- Hermes Desktop bridge is unavailable
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/bf70bf690f668e82.
Report an issue: GitHub.