NousResearch/hermes-agent · error
Hermes gateway unavailable
Error message
Hermes gateway unavailable
What it means
Thrown by the wake-word store's gatewayRequester when it needs to send a wake.* RPC but $gateway.get() returns null — i.e. no gateway client instance has been created/connected yet. Wake-word start/stop/status all funnel through this requester, so any call before the gateway singleton exists fails fast with this message.
Source
Thrown at apps/desktop/src/store/wake-word.ts:145
max_input_channels?: number
name?: string
selector?: number | string | null
}
/** Minimal requester shape — satisfied by both `useGatewayRequest`'s
* `requestGateway` and the `$gateway` instance wrapper below. */
export type WakeRequester = <T>(method: string, params?: Record<string, unknown>) => Promise<T>
// First-use wake.start lazy-installs the detection engine (onnxruntime is a
// large wheel) — that legitimately takes minutes. The default 30s WS timeout
// fired mid-install, leaving a dead button that went blue on its own later.
const WAKE_START_TIMEOUT_MS = 180_000
const gatewayRequester: WakeRequester = async <T>(method: string, params: Record<string, unknown> = {}) => {
const gateway = $gateway.get()
if (!gateway) {
throw new Error('Hermes gateway unavailable')
}
return method === 'wake.start'
? gateway.request<T>(method, params, WAKE_START_TIMEOUT_MS)
: gateway.request<T>(method, params)
}
// Friendly text for the gateway's wake refusal codes (mirrors the TUI's
// START_REASON_TEXT). Unknown codes fall through raw so new server-side
// codes stay visible instead of silently disappearing.
const REASON_TEXT: Record<string, string> = {
disabled: 'click to enable',
disabled_for_surface: 'scoped to another surface (config wake_word.surface)',
not_owner: 'another surface owns the listener',
owned: 'another surface owns the listener',
unavailable: 'unavailable'
}
View on GitHub (pinned to c896c09c42)
Solutions
- Wait for the gateway-ready signal ($gateway populated / connection established) before enabling or invoking wake-word controls.
- Check the gateway connection in Settings -> Gateway; reconnect if the backend URL or auth is broken.
- Bind the wake-word toggle's disabled state to gateway availability so the call cannot be made early.
- If it persists, restart the app/backend — the gateway instance may have failed to initialize.
Example fix
// before
const res = await wakeRequester('wake.start')
// after
if (!$gateway.get()) { throw new Error('Connect to the gateway first') }
const res = await wakeRequester('wake.start') Defensive patterns
Strategy: type-guard
Validate before calling
import { $gateway } from '.../store'
if (!$gateway.get()) {
// disable wake-word UI instead of calling
} Type guard
function gatewayReady(): boolean {
return $gateway.get() !== null
} Try / catch
try {
await startWakeWord()
} catch (e) {
if (e instanceof Error && e.message === 'Hermes gateway unavailable') awaitGatewayReadyThenRetry()
else throw e
} Prevention
- Derive wake-word control disabled state from $gateway availability.
- Subscribe to gateway connect/disconnect to re-evaluate wake-word state.
- Never call wake.* from module init — wait for the ready event.
When it happens
Trigger: Calling wake.start / wake.stop / wake.status through the wake-word UI or store before the desktop app established its gateway WebSocket connection, or after the gateway client was torn down on disconnect.
Common situations: App startup race (wake-word widget mounts before gateway connect completes), remote backend URL not reachable so $gateway stays null, or a session-end path that cleared the gateway atom.
Related errors
- Remote Hermes gateway is selected, but no session token is s
- Gateway did not return a WS ticket.
- Remote gateway session token is required.
- HERMES_DESKTOP_REMOTE_URL is set but HERMES_DESKTOP_REMOTE_T
- Reached the gateway over HTTP, but the live WebSocket (/api/
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/728483b9e718b239.
Report an issue: GitHub.