{"record":{"id":"558b6b09c63a8444","repo":"NousResearch/hermes-agent","slug":"hermes-desktop-bridge-is-unavailable","errorCode":null,"errorMessage":"Hermes Desktop bridge is unavailable","messagePattern":"Hermes Desktop bridge is unavailable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/src/lib/desktop-fs.ts","lineNumber":54,"sourceCode":"export function isDesktopFsRemoteMode() {\n  return $connection.get()?.mode === 'remote'\n}\n\n// Active profile for FS/git REST calls. Without it the Electron api bridge\n// hits the primary (local) backend even when the user switched to a remote profile.\nexport function desktopFsProfile(): string | undefined {\n  return $connection.get()?.profile || undefined\n}\n\nfunction fsPath(endpoint: string, filePath: string) {\n  return `/api/fs/${endpoint}?path=${encodeURIComponent(filePath)}`\n}\n\nfunction bridge() {\n  const desktop = window.hermesDesktop\n\n  if (!desktop) {\n    throw new Error('Hermes Desktop bridge is unavailable')\n  }\n\n  return desktop\n}\n\nfunction remoteFsApi<T>(path: string, body?: Record<string, unknown>): Promise<T> {\n  return bridge().api<T>(\n    body ? { body, method: 'POST', path, profile: desktopFsProfile() } : { path, profile: desktopFsProfile() }\n  )\n}\n\nexport async function readDesktopDir(path: string): Promise<HermesReadDirResult> {\n  if (!isDesktopFsRemoteMode()) {\n    return bridge().readDir(path)\n  }\n\n  return remoteFsApi<HermesReadDirResult>(fsPath('list', path))\n}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/src/lib/desktop-fs.ts#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check for the bridge before use: `if (!window.hermesDesktop) { showUnsupported(); return }`.","In tests, install a minimal stub: `(window as any).hermesDesktop = { api: vi.fn() }`.","Keep desktop-fs calls inside desktop-only surfaces; for dual environments use isDesktopFsRemoteMode() and the remote REST path with the bridge check intact.","If the bridge is unexpectedly missing inside Electron, verify the preload script is attached in the BrowserWindow webPreferences."],"exampleFix":"// before\nconst entries = await readDesktopDir(path)\n\n// after\nfunction ensureBridge() {\n  if (!window.hermesDesktop) throw new Error('Desktop shell required for filesystem access')\n}\nensureBridge()\nconst entries = await readDesktopDir(path)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"type DesktopBridge = NonNullable<Window['hermesDesktop']>\nfunction desktopBridgeOrThrow(): DesktopBridge {\n  const d = window.hermesDesktop\n  if (!d) throw new Error('Hermes Desktop bridge is unavailable')\n  return d\n}","tryCatchPattern":"if (!window.hermesDesktop) { renderUnavailable('File browsing requires the desktop app'); return }\ntry { await readDesktopDir(path) } catch (e) { if (e instanceof Error && e.message === 'Hermes Desktop bridge is unavailable') setFsUnavailable() else throw e }","preventionTips":["Mount desktop-fs-dependent UI only in desktop contexts","Centralize the bridge check at feature mount (one guard per surface)","Stub hermesDesktop in jsdom tests","If this fires inside Electron, check preload script attachment before debugging callers"],"tags":["desktop","electron","bridge","filesystem","environment"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}