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
- 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.
- 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.
- If developing in a browser, skip the QR probe flow or substitute a direct websocket connector for local testing.
- 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
- Run stage-pocket server-channel-qr flows only inside the native shell that injects the host bridge.
- Surface a dedicated 'bridge missing' message in the UI before calling probeServerChannelQrPayload.
- In tests, stub window.AiriHostBridge before exercising any connector code.
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
- AIRI host websocket bridge is unavailable
- No candidate server channel URL was reachable. ${errors.join
- Beat Sync is not available in Stage Pocket
- Godot stage bridge is not connected.
- No WebSocket constructor is available. Pass `wsConstructor`
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/bdf2f46ac45a75f1.
Report an issue: GitHub.