moeru-ai/airi · error · Error

No candidate server channel URL was reachable. ${errors.join

Error message

No candidate server channel URL was reachable. ${errors.join('; ')}

What it means

Thrown by probeServerChannelQrPayload after every candidate URL in payload.urls failed its websocket handshake/connect probe. Each per-URL failure is captured via errorMessageFrom and aggregated into the message joined by '; '. The host bridge is present (errors [0]/[1] did not fire), but no URL produced a working connection within the 2.5s connect timeout.

Source

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

      connectTimeoutMs: 2_000,
      name: WebSocketEventSource.StageWeb,
      token: payload.authToken,
      url,
      connector: createTextProtocolConnector(connector),
    })

    try {
      await client.connect({ timeout: 2_500 })
      client.close()
      return url
    }
    catch (error) {
      client.close()
      errors.push(`${url}: ${errorMessageFrom(error) ?? 'Unknown websocket probe error'}`)
    }
  }

  throw new Error(`No candidate server channel URL was reachable. ${errors.join('; ')}`)
}

View on GitHub (pinned to 27111382b4)

Solutions

  1. Read the aggregated per-URL reasons in the message to identify whether the failure is connection-refused, timeout, TLS, or auth.
  2. Ensure the desktop AIRI app is running and its server-channel service is started, and confirm the advertised host:port matches the phone's reachable network path.
  3. Verify both devices are on the same LAN/subnet and that the server-channel port (default 6121) is not firewalled.
  4. For wss URLs, install/trust the desktop's generated certificate on the mobile device, or disable TLS for local testing.
  5. Re-scan the QR if the authToken may have rotated.
Defensive patterns

Strategy: retry

Try / catch

try {
  await probeServerChannelQrPayload(payload)
}
catch (error) {
  const msg = errorMessageFrom(error) ?? ''
  if (msg.startsWith('No candidate server channel URL was reachable.')) {
    // parse the per-URL reasons, guide the user (start desktop, check LAN, trust cert)
  }
  throw error
}

Prevention

When it happens

Trigger: The QR payload lists one or more ws/wss URLs and client.connect({ timeout: 2_500 }) rejected for each — causes include the desktop server channel not running, wrong port, firewall blocking the LAN IP, TLS/cert mismatch on wss, expired/invalid authToken, or the host not bound on the advertised interface.

Common situations: Desktop AIRI app not running or server-channel service not started; phone on a different subnet than the desktop; port 6121 blocked by firewall; wss URL with a self-signed cert the webview rejects; authToken in the QR no longer valid after a desktop restart; IPv6 link-local address advertised without zone id.

Related errors


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