tinyhumansai/openhuman · error

Socket not connected — no client ID for event routing

Error message

Socket not connected — no client ID for event routing

What it means

Thrown by chatSend() before sending: the chat turn is dispatched via 'openhuman.channel_web_chat' and the core streams the turn's events (tool_call, chat_done, chat_error) back over the socket keyed by client_id = socket.id. If socketService.getSocket() returns no socket or the socket has no id, the turn would be launched blind with no event lane, so the client refuses to send.

Source

Thrown at app/src/services/chatService.ts:1268

  queueMode?: QueueMode | null;
}

/**
 * Send a chat message via core RPC.
 *
 * The Rust core spawns the agent loop asynchronously and streams events
 * (tool_call, tool_result, chat_done, chat_error) back over the socket
 * connection using the `client_id` (socket ID) for routing.
 *
 * Returns the turn's `request_id` (from the RPC ack) when the core provides
 * one — used by `parallel` sends to register the forked turn's stream lane.
 * `undefined` if the ack carried no id.
 */
export async function chatSend(params: ChatSendParams): Promise<string | undefined> {
  const socket = socketService.getSocket();
  const clientId = socket?.id;
  if (!clientId) {
    throw new Error('Socket not connected — no client ID for event routing');
  }

  const result = await callCoreRpc({
    method: 'openhuman.channel_web_chat',
    params: {
      client_id: clientId,
      thread_id: params.threadId,
      message: params.message,
      model_override: params.model ?? undefined,
      profile_id: params.profileId ?? undefined,
      locale: params.locale ?? undefined,
      speak_reply: params.speakReply ?? undefined,
      source: params.source ?? undefined,
      session_id: params.sessionId ?? undefined,
      queue_mode: params.queueMode ?? undefined,
    },
  });

View on GitHub (pinned to a221052e0d)

Solutions

  1. Gate the send action on socket connectivity (the Redux socket slice / socketService state) and disable/queue the composer while disconnected
  2. Trigger or await socket reconnect before retrying the send — the message text should be preserved in the composer
  3. If it persists, restart the core and let the app reconnect (Settings -> Restart Core drives the full reconnect path)
  4. In tests, mock socketService.getSocket to return { id: 'test-socket' }

Example fix

// before
const requestId = await chatSend({ threadId, message });

// after
const socket = socketService.getSocket();
if (!socket?.id) {
  setPendingMessage(message); // queue until reconnect
  await socketService.reconnect();
}
const requestId = await chatSend({ threadId, message });
Defensive patterns

Strategy: validation

Validate before calling

const socket = socketService.getSocket();
if (!socket?.id) {
  setQueuedMessage(params); // preserve the draft
  await ensureSocketConnected(); // reconnect before dispatching the turn
}
await chatSend(params);

Type guard

function hasSocketId(s: unknown): s is { id: string } {
  return !!s && typeof s === 'object' && typeof (s as { id?: unknown }).id === 'string' && (s as { id: string }).id.length > 0;
}

Try / catch

try {
  return await chatSend(params);
} catch (e) {
  if (e instanceof Error && e.message.includes('no client ID')) {
    await socketService.reconnect();
    return chatSend(params); // message preserved by the caller
  }
  throw e;
}

Prevention

When it happens

Trigger: User hits send while the socket is disconnected: core was restarted, network dropped, auth expired and the socket closed, or the app is in a state where the socket service never connected. Also fires in tests that call chatSend without mocking a connected socket.

Common situations: Socket reconnect lag after core restart (user types faster than reconnect); laptop sleep/resume; backend/auth expiry closing the socket; UI send button not gated on connectivity state.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/913406329ba9c400. Report an issue: GitHub.