NousResearch/hermes-agent · error

Hermes Desktop bridge is unavailable

Error message

Hermes Desktop bridge is unavailable

What it means

Thrown by bridge() in apps/desktop/src/lib/desktop-fs.ts:54 when `window.hermesDesktop` is undefined — the Electron preload bridge is not present. Every desktop-fs helper (read/write/rename/trash/diff) funnels through this guard, so any filesystem call made outside the desktop shell, before preload injection, or in a plain browser/jsdom context fails here first.

Source

Thrown at apps/desktop/src/lib/desktop-fs.ts:54

export function isDesktopFsRemoteMode() {
  return $connection.get()?.mode === 'remote'
}

// Active profile for FS/git REST calls. Without it the Electron api bridge
// hits the primary (local) backend even when the user switched to a remote profile.
export function desktopFsProfile(): string | undefined {
  return $connection.get()?.profile || undefined
}

function fsPath(endpoint: string, filePath: string) {
  return `/api/fs/${endpoint}?path=${encodeURIComponent(filePath)}`
}

function bridge() {
  const desktop = window.hermesDesktop

  if (!desktop) {
    throw new Error('Hermes Desktop bridge is unavailable')
  }

  return desktop
}

function remoteFsApi<T>(path: string, body?: Record<string, unknown>): Promise<T> {
  return bridge().api<T>(
    body ? { body, method: 'POST', path, profile: desktopFsProfile() } : { path, profile: desktopFsProfile() }
  )
}

export async function readDesktopDir(path: string): Promise<HermesReadDirResult> {
  if (!isDesktopFsRemoteMode()) {
    return bridge().readDir(path)
  }

  return remoteFsApi<HermesReadDirResult>(fsPath('list', path))
}

View on GitHub (pinned to c896c09c42)

Solutions

  1. Check for the bridge before use: `if (!window.hermesDesktop) { showUnsupported(); return }`.
  2. In tests, install a minimal stub: `(window as any).hermesDesktop = { api: vi.fn() }`.
  3. Keep desktop-fs calls inside desktop-only surfaces; for dual environments use isDesktopFsRemoteMode() and the remote REST path with the bridge check intact.
  4. If the bridge is unexpectedly missing inside Electron, verify the preload script is attached in the BrowserWindow webPreferences.

Example fix

// before
const entries = await readDesktopDir(path)

// after
function ensureBridge() {
  if (!window.hermesDesktop) throw new Error('Desktop shell required for filesystem access')
}
ensureBridge()
const entries = await readDesktopDir(path)
Defensive patterns

Strategy: type-guard

Type guard

type DesktopBridge = NonNullable<Window['hermesDesktop']>
function desktopBridgeOrThrow(): DesktopBridge {
  const d = window.hermesDesktop
  if (!d) throw new Error('Hermes Desktop bridge is unavailable')
  return d
}

Try / catch

if (!window.hermesDesktop) { renderUnavailable('File browsing requires the desktop app'); return }
try { await readDesktopDir(path) } catch (e) { if (e instanceof Error && e.message === 'Hermes Desktop bridge is unavailable') setFsUnavailable() else throw e }

Prevention

When it happens

Trigger: Calling readDesktopDir / writeDesktopFileText / any desktop-fs helper in the web dashboard or a non-Electron browser tab; invoking during early renderer startup before the preload script runs; unit tests without a hermesDesktop stub; a web preview build that imports desktop-fs.

Common situations: Shared components accidentally imported into the web build calling desktop-fs; jsdom/vitest runs; the preload bundle failing to load after an app update (disabled webSecurity flags, corrupted asar); opening the renderer URL in an external browser for debugging.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/558b6b09c63a8444. Report an issue: GitHub.