NousResearch/hermes-agent · error · Error
Desktop bridge unavailable
Error message
Desktop bridge unavailable
What it means
Thrown by openHtmlInBrowser in the desktop right-rail artifact preview. It composes an HTML document and needs the Electron preload bridge (window.hermesDesktop) to write a temp file and hand a file:// URL to the OS browser — a blob/data URL cannot cross into the default browser. The error means the bridge object or one of its required methods (saveImageBuffer, openExternal) is missing, i.e. the code is running outside the expected Electron preload context or against an older preload that lacks those methods.
Source
Thrown at apps/desktop/src/app/chat/right-rail/preview-artifact.tsx:53
}
return [
'<!doctype html>',
'<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">',
'<style>body{margin:0;font-family:system-ui,sans-serif}</style></head><body>',
content,
'</body></html>'
].join('\n')
}
/** Write the composed document to a real temp file through the existing
* buffer-save IPC, then hand it to the OS browser. A blob/data URL can't
* cross into the OS default browser, so a file on disk is the honest path. */
async function openHtmlInBrowser(content: string): Promise<void> {
const bridge = window.hermesDesktop
if (!bridge?.saveImageBuffer || !bridge.openExternal) {
throw new Error('Desktop bridge unavailable')
}
const bytes = new TextEncoder().encode(composeArtifactHtml(content))
const path = await bridge.saveImageBuffer(bytes, '.html')
if (!path) {
throw new Error('Could not write artifact file')
}
const fileUrl = `file://${path.startsWith('/') ? '' : '/'}${path.replace(/\\/g, '/')}`
if (bridge.openPreviewInBrowser) {
await bridge.openPreviewInBrowser(fileUrl)
return
}
await bridge.openExternal(fileUrl)View on GitHub (pinned to c896c09c42)
Solutions
- Ensure the renderer runs inside the Electron app with the current preload script that installs window.hermesDesktop (saveImageBuffer + openExternal).
- If running in a plain browser context, gate the 'open in browser' button behind a bridge availability check instead of calling openHtmlInBrowser.
- Rebuild/reinstall the desktop app so preload and renderer versions match.
- In tests, stub window.hermesDesktop with the required methods.
Example fix
// before
const bridge = window.hermesDesktop
if (!bridge?.saveImageBuffer || !bridge.openExternal) {
throw new Error('Desktop bridge unavailable')
}
// after — feature-detect at the UI layer and disable the action
const bridgeAvailable = Boolean(
window.hermesDesktop?.saveImageBuffer && window.hermesDesktop?.openExternal
)
// render the button only when bridgeAvailable; keep the throw as an invariant guard Defensive patterns
Strategy: type-guard
Validate before calling
const bridge = window.hermesDesktop
const canOpenInBrowser = Boolean(bridge?.saveImageBuffer && bridge?.openExternal)
if (!canOpenInBrowser) {
// hide/disable the 'open in browser' action instead of throwing at click time
} Type guard
interface DesktopBridge {
saveImageBuffer(bytes: Uint8Array, ext: string): Promise<string>
openExternal(url: string): Promise<void>
openPreviewInBrowser?(url: string): Promise<void>
}
const isDesktopBridge = (b: unknown): b is DesktopBridge =>
typeof b === 'object' && b !== null &&
typeof (b as DesktopBridge).saveImageBuffer === 'function' &&
typeof (b as DesktopBridge).openExternal === 'function' Try / catch
try {
await openHtmlInBrowser(content)
} catch (error) {
if (error instanceof Error && error.message === 'Desktop bridge unavailable') {
// degrade gracefully: fall back to in-pane HTML rendering
renderInline(content)
} else {
throw error
}
} Prevention
- Feature-detect window.hermesDesktop before rendering OS-browser actions
- Keep preload and renderer bundles versioned/deployed together
- Mock the bridge in tests so components assume its presence only inside Electron
When it happens
Trigger: Rendering the artifact 'open in browser' action in a non-Electron host (plain browser, web dashboard, tests/jsdom) where window.hermesDesktop is undefined; an Electron runtime whose preload script predates the saveImageBuffer bridge method; contextIsolation/sandbox settings preventing the bridge from being exposed on window.
Common situations: Reusing desktop components in the web dashboard where the preload bridge does not exist; upgrading the renderer without upgrading the Electron main/preload bundle; running component tests without mocking window.hermesDesktop.
Related errors
- Desktop preview browser bridge is unavailable
- Secure token storage is unavailable (no OS keyring service w
- Failed to encrypt the remote gateway token for secure storag
- An update is already in progress.
- Invalid preview URL
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/beb8b217b6b604bf.
Report an issue: GitHub.