{"record":{"id":"eee3a62ea3e0261d","repo":"NousResearch/hermes-agent","slug":"gateway-not-connected-method","errorCode":null,"errorMessage":"gateway not connected: ${method}","messagePattern":"gateway not connected: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ui-tui/src/gatewayClient.ts","lineNumber":686,"sourceCode":"  private async ensureAttachedWebSocket(method: string): Promise<WebSocket> {\n    if (!this.attachUrl) {\n      throw new Error('gateway not running')\n    }\n\n    if (!this.ws || this.ws.readyState === WS_CLOSED || this.ws.readyState === WS_CLOSING) {\n      this.start()\n    }\n\n    if (this.ws?.readyState === WS_CONNECTING) {\n      try {\n        await this.wsConnectPromise\n      } catch (err) {\n        throw err instanceof Error ? err : new Error(String(err))\n      }\n    }\n\n    if (!this.ws || this.ws.readyState !== WS_OPEN) {\n      throw new Error(`gateway not connected: ${method}`)\n    }\n\n    return this.ws\n  }\n\n  private requestOverWebSocket<T = unknown>(method: string, params: Record<string, unknown> = {}): Promise<T> {\n    return this.ensureAttachedWebSocket(method).then(\n      ws =>\n        new Promise<T>((resolve, reject) => {\n          const id = `r${++this.reqId}`\n          const timeout = setTimeout(this.onTimeout, REQUEST_TIMEOUT_MS, id)\n\n          timeout.unref?.()\n          this.pending.set(id, {\n            id,\n            method,\n            reject,\n            resolve: v => resolve(v as T),","sourceCodeStart":668,"sourceCodeEnd":704,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/ui-tui/src/gatewayClient.ts#L668-L704","documentation":"Thrown by GatewayClient.ensureAttachedWebSocket after it attempted (re)connection: attachUrl exists, start() was called if needed, the CONNECTING promise was awaited, but the WebSocket still is not in OPEN state — the connection attempt failed, closed immediately, or is stuck. The method name is included to identify which RPC was blocked.","triggerScenarios":"Any gateway.rpc(...) while the WebSocket handshake fails: backend not listening on the port/URL, auth rejection closing the socket, network unreachable, or the socket closing between the connect-await and the state check.","commonSituations":"Gateway process crashed or still booting when the TUI connects, wrong port/URL in config, firewall/proxy blocking WS upgrade, or token auth failing so the server closes the connection instantly.","solutions":["Verify the gateway is actually running and listening on the configured URL/port.","Check gateway logs for handshake/auth failures; fix credentials if the socket is closed on auth.","Retry after the gateway reports ready; add backoff/retry around RPCs issued during startup.","Confirm no proxy in between strips WebSocket upgrades (wss/WS upgrade headers)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"function wsOpen(client: GatewayClient): boolean {\n  return client.ws?.readyState === WebSocket.OPEN\n}\n\n// issue RPCs only when wsOpen(client) is true, or after the ready event","typeGuard":"function isWsOpen(ws: WebSocket | undefined): ws is WebSocket {\n  return ws !== undefined && ws.readyState === WebSocket.OPEN\n}","tryCatchPattern":"try {\n  await client.rpc(method, params)\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('gateway not connected')) {\n    await backoff(500)\n    return client.rpc(method, params) // single retry after reconnect\n  }\n  throw e\n}","preventionTips":["Confirm the gateway process is listening before connecting (health check/port probe).","Add bounded retry-with-backoff for RPCs issued during gateway startup.","Watch the client's exit/close events and pause RPC traffic until reconnected."],"tags":["tui","gateway","websocket","connection","retry"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}