{"record":{"id":"7e78086dd5cce684","repo":"NousResearch/hermes-agent","slug":"hermes-desktop-bridge-is-unavailable-7e7808","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-git.ts","lineNumber":25,"sourceCode":"  HermesReviewList,\n  HermesReviewShipInfo\n} from '@/global'\n\nimport { desktopFsProfile, isDesktopFsRemoteMode } from './desktop-fs'\n\n// Remote-aware git facade. Locally the desktop runs git through Electron\n// (window.hermesDesktop.git); on a remote gateway that's the wrong filesystem,\n// so we mirror the same surface over the dashboard REST API (/api/git/*) — the\n// coding rail, worktree lanes, review pane, and branch ops then act on the\n// BACKEND repo where sessions actually run. Mirrors desktop-fs.ts.\n\ntype GitBridge = NonNullable<NonNullable<Window['hermesDesktop']>['git']>\n\nfunction desktopApi<T>(path: string, body?: Record<string, unknown>): Promise<T> {\n  const desktop = window.hermesDesktop\n\n  if (!desktop) {\n    throw new Error('Hermes Desktop bridge is unavailable')\n  }\n\n  return desktop.api<T>(\n    body ? { body, method: 'POST', path, profile: desktopFsProfile() } : { path, profile: desktopFsProfile() }\n  )\n}\n\nfunction gitGet<T>(route: string, params: Record<string, boolean | null | string | undefined>): Promise<T> {\n  const query = new URLSearchParams()\n\n  for (const [key, value] of Object.entries(params)) {\n    if (value !== null && value !== undefined) {\n      query.set(key, String(value))\n    }\n  }\n\n  return desktopApi<T>(`/api/git/${route}?${query.toString()}`)\n}","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/src/lib/desktop-git.ts#L7-L43","documentation":"Thrown by desktopApi() in apps/desktop/src/lib/desktop-git.ts:25 when `window.hermesDesktop` is absent. desktop-git routes git operations through the Electron bridge (`desktop.api`) — locally via window.hermesDesktop.git, remotely via the dashboard REST /api/git/* mirror — but both shapes still need the base desktop bridge to make the api call. Outside the desktop shell there is no transport at all.","triggerScenarios":"Any desktop-git helper call (status, file-diff, branch ops, worktree lanes, review pane) executed in a browser/web build or jsdom test without a hermesDesktop stub; calling before preload injection completes.","commonSituations":"Git-dependent UI (coding rail, review pane) rendered in the web dashboard; tests for git facades without bridge mocks; debugging the renderer in an external browser.","solutions":["Feature-gate git surfaces on the bridge: `if (!window.hermesDesktop) return disabledState`.","Stub window.hermesDesktop = { api: vi.fn() } in unit tests of git-dependent components.","Ensure git components are only mounted in desktop-only routes.","Verify preload attachment if this fires inside Electron proper."],"exampleFix":"// before\nconst status = await gitStatus(repoRoot)\n\n// after\nif (!window.hermesDesktop) {\n  setGitUnavailable('Git tooling is available only in the Hermes desktop app')\n  return\n}\nconst status = await gitStatus(repoRoot)","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function hasDesktopBridge(w: Window): w is Window & { hermesDesktop: NonNullable<Window['hermesDesktop']> } {\n  return Boolean((w as any).hermesDesktop)\n}","tryCatchPattern":"if (!hasDesktopBridge(window)) { setGitUnavailable('Git tooling is desktop-only'); return }\ntry { const status = await gitStatus(repoRoot) } catch (e) { if (e instanceof Error && e.message === 'Hermes Desktop bridge is unavailable') setGitUnavailable() else throw e }","preventionTips":["Mount git surfaces (coding rail, review pane, worktree lanes) only in desktop routes","One shared capability check per surface, not per operation","Mock window.hermesDesktop.api for git facade tests","Remote git goes through /api/git/* but still requires the base bridge — never assume browser fetch works here"],"tags":["desktop","git","electron","bridge","environment"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}