{"record":{"id":"34473b3ec6597ae0","repo":"slopus/happy","slug":"not-connected-to-gateway","errorCode":null,"errorMessage":"Not connected to gateway","messagePattern":"Not connected to gateway","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/happy-cli/src/openclaw/OpenClawSocket.ts","lineNumber":154,"sourceCode":"      this.pairingRequestId = null;\n      this.doConnect();\n    }\n  }\n\n  onStatusChange(handler: OpenClawStatusHandler): () => void {\n    this.statusListeners.add(handler);\n    handler(this.status, undefined, { pairingRequestId: this.pairingRequestId ?? undefined });\n    return () => this.statusListeners.delete(handler);\n  }\n\n  onEvent(handler: OpenClawEventHandler): () => void {\n    this.eventListeners.add(handler);\n    return () => this.eventListeners.delete(handler);\n  }\n\n  async request<T = unknown>(method: string, params?: unknown, timeoutMs = 15000): Promise<T> {\n    if (!this.ws || this.status !== 'connected') {\n      throw new Error('Not connected to gateway');\n    }\n\n    const id = randomUUID();\n    const frame = { type: 'req', id, method, params };\n\n    return new Promise((resolve, reject) => {\n      const timeout = setTimeout(() => {\n        this.pending.delete(id);\n        reject(new Error(`Request timeout: ${method}`));\n      }, timeoutMs);\n\n      this.pending.set(id, {\n        resolve: (value) => {\n          clearTimeout(timeout);\n          resolve(value as T);\n        },\n        reject: (error) => {\n          clearTimeout(timeout);","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/slopus/happy/blob/b824cd0a4681d41af631a8e422a813873e4455b0/packages/happy-cli/src/openclaw/OpenClawSocket.ts#L136-L172","documentation":"OpenClawSocket.request() sends a JSON-RPC-style request frame over the gateway WebSocket. It validates that the WebSocket exists and status === 'connected' before generating a request ID; otherwise the request could never receive a response. Called by sendMessage and abortRun, so both fail with this error when the transport is down.","triggerScenarios":"Calling request() (directly or via sendMessage/abortRun) when this.ws is null/undefined or this.status !== 'connected' — before connection completes, after a disconnect, or during a reconnect window.","commonSituations":"Aborting a run after the gateway connection dropped; sending a message concurrently with a reconnect; timeout of the initial connect causing later calls to run against a closed socket.","solutions":["Wait for the socket's connected event/status before issuing requests","Reconnect the socket and retry the request once connection is re-established","Check gateway availability and logs; the request timeout (15s default) may indicate the connection died earlier"],"exampleFix":"// before\nawait socket.sendMessage(msg);\n// after\nif (socket.status !== 'connected') {\n  await socket.connect(gatewayConfig);\n}\nawait socket.sendMessage(msg);","handlingStrategy":"validation","validationCode":"if (!socket.isConnected()) {\n  await socket.connect(gatewayConfig);\n}","typeGuard":"const canRequest = (s: OpenClawSocket): boolean => s.ws !== null && s.status === 'connected';","tryCatchPattern":"try {\n  return await socket.request<T>(method, params);\n} catch (err) {\n  if (err.message === 'Not connected to gateway') {\n    await socket.connect(gatewayConfig);\n    return await socket.request<T>(method, params);\n  }\n  throw err;\n}","preventionTips":["Await the socket 'connected' status before issuing any request","Add a reconnect-with-backoff wrapper around request/sendMessage/abortRun","Treat the 15s request timeout as a signal the connection may have died"],"tags":["openclaw","websocket","connection","rpc"],"backgroundTag":"not-connected","analyzedSha":"b824cd0a4681d41af631a8e422a813873e4455b0","analyzedAt":"2026-08-31T23:12:36.205Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}