moeru-ai/airi · error · Error

AIRI host websocket bridge is unavailable

Error message

AIRI host websocket bridge is unavailable

What it means

Thrown by probeServerChannelQrPayload in stage-pocket when no candidate QR URL yields a usable host websocket connector. The connector comes from getHostWebSocketConnector, which returns undefined unless a native host bridge object (window.AiriHostBridge or window.webkit.messageHandlers.airiHostBridge) has been injected by the embedding native shell. The check runs once across payload.urls, but the bridge presence is global, so absence fails every URL identically.

Source

Thrown at apps/stage-pocket/src/modules/server-channel-qr-probe.ts:10

import type { ServerChannelQrPayload } from '@proj-airi/stage-shared/server-channel-qr'

import { errorMessageFrom } from '@moeru/std'
import { Client, createTextProtocolConnector, WebSocketEventSource } from '@proj-airi/server-sdk'

import { getHostWebSocketConnector } from './websocket-bridge'

export async function probeServerChannelQrPayload(payload: ServerChannelQrPayload) {
  if (!payload.urls.some(url => getHostWebSocketConnector(url))) {
    throw new Error('AIRI host websocket bridge is unavailable')
  }

  const errors: string[] = []

  for (const url of payload.urls) {
    const connector = getHostWebSocketConnector(url)
    if (!connector) {
      throw new Error('AIRI host websocket bridge is unavailable')
    }

    const client = new Client({
      autoConnect: false,
      autoReconnect: false,
      connectTimeoutMs: 2_000,
      name: WebSocketEventSource.StageWeb,
      token: payload.authToken,
      url,
      connector: createTextProtocolConnector(connector),

View on GitHub (pinned to 27111382b4)

Solutions

  1. Run the probe only inside the host native shell that injects window.AiriHostBridge (or the webkit.messageHandlers.airiHostBridge equivalent); verify the bridge is present before calling probeServerChannelQrPayload.
  2. Gate the call with a pre-check: if (!window.AiriHostBridge && !window.webkit?.messageHandlers?.airiHostBridge) surface a dedicated 'host bridge missing' message instead of invoking the probe.
  3. If developing in a browser, skip the QR probe flow or substitute a direct websocket connector for local testing.
  4. Confirm the native shell's bridge registration code runs before the renderer calls any server-channel-qr logic.

Example fix

// before
await probeServerChannelQrPayload(payload)

// after
const bridgePresent = !!(window.AiriHostBridge || window.webkit?.messageHandlers?.airiHostBridge)
if (!bridgePresent) {
  throw new Error('Open this flow inside the AIRI host app: native websocket bridge not injected.')
}
await probeServerChannelQrPayload(payload)
Defensive patterns

Strategy: validation

Validate before calling

function isHostBridgeAvailable(): boolean {
  return !!(window.AiriHostBridge || window.webkit?.messageHandlers?.airiHostBridge)
}

// before probing:
if (!isHostBridgeAvailable()) {
  throw new Error('Open this flow inside the AIRI host app: native websocket bridge not injected.')
}

Type guard

function isHostBridgeAvailable(): boolean {
  return !!(window.AiriHostBridge || window.webkit?.messageHandlers?.airiHostBridge)
}

Prevention

When it happens

Trigger: Calling probeServerChannelQrPayload(payload) from a stage-pocket webview where the native shell has not injected the AiriHostBridge / webkit.messageHandlers.airiHostBridge objects. This occurs before any websocket connection attempt, so it fails fast regardless of how many URLs the QR payload carries.

Common situations: Running stage-pocket in a plain browser tab or Capacitor webview that lacks the native bridge wiring; opening the pocket app before the native module registered its message handler; debugging the renderer outside the host app shell; a native shell regression that renames or removes the injected global.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/bdf2f46ac45a75f1. Report an issue: GitHub.