moeru-ai/airi · error · Error

No reachable private LAN address is available for the curren

Error message

No reachable private LAN address is available for the current server channel host.

What it means

Thrown by getServerChannelQrPayload when getServerChannelQrHosts returns an empty array, so no ws/wss URL can be constructed for the QR payload. getServerChannelQrHosts returns [] when the configured hostname is a loopback address (localhost/127.0.0.1/::1), and returns only non-loopback connection hosts when hostname is 0.0.0.0 — which is also empty if the server only exposes loopback interfaces.

Source

Thrown at apps/stage-tamagotchi/src/main/services/airi/channel-server/index.ts:114

  }

  return [config.hostname]
}

function createServerChannelUrl(protocol: 'ws' | 'wss', host: string) {
  const urlHost = isIP(host) === 6 ? `[${host}]` : host
  // TODO: Deduplicate the server channel websocket path with `packages/server-runtime/src/index.ts`
  // and `packages/server-sdk/src/client.ts` so this does not rely on three separate `/ws` literals.
  return `${protocol}://${urlHost}:${getServerChannelPort()}/ws`
}

function getServerChannelQrPayload(config: ElectronServerChannelConfig, serverChannel: Server) {
  const protocol = config.tlsConfig ? 'wss' : 'ws'
  const urls = getServerChannelQrHosts(config, serverChannel)
    .map(host => createServerChannelUrl(protocol, host))

  if (!urls.length) {
    throw new Error('No reachable private LAN address is available for the current server channel host.')
  }

  return createServerChannelQrPayload({
    type: 'airi:server-channel',
    version: 1,
    urls,
    authToken: config.authToken,
  })
}

async function getChannelServerConfig(): Promise<ElectronServerChannelConfig> {
  const config = channelServerConfigStore.get() || { hostname: '127.0.0.1', authToken: '', tlsConfig: null }

  return {
    hostname: config.hostname || '127.0.0.1',
    authToken: config.authToken || '',
    tlsConfig: config.tlsConfig || null,
  }

View on GitHub (pinned to 27111382b4)

Solutions

  1. Set the server-channel hostname to '0.0.0.0' (bind all interfaces) and ensure the machine has at least one non-loopback private LAN address.
  2. Or set the hostname explicitly to the machine's LAN IP (e.g. 192.168.x.x).
  3. Confirm the network interface is up and not VPN-only before requesting the QR payload.
  4. If pairing is unnecessary on this host, avoid calling electronGetServerChannelQrPayload.

Example fix

// before: hostname '127.0.0.1' -> getServerChannelQrHosts returns []

// after
await applyServerChannelConfig({ hostname: '0.0.0.0' })
// now non-loopback LAN addresses are advertised in the QR
Defensive patterns

Strategy: validation

Validate before calling

function hasLanAddress(config, serverChannel): boolean {
  return getServerChannelQrHosts(config, serverChannel).length > 0
}

if (!hasLanAddress(config, serverChannel)) {
  // prompt user to set hostname '0.0.0.0' or a LAN IP before requesting the QR
}

Type guard

function isNonLoopbackHost(host: string): boolean {
  return !['localhost', '127.0.0.1', '::1'].includes(host)
}

Prevention

When it happens

Trigger: Requesting electronGetServerChannelQrPayload while hostname is '127.0.0.1' (the default) or another loopback value; or hostname is '0.0.0.0' but getLocalIPs/connection hosts yielded only loopback addresses (e.g. machine on a VPN or isolated network with no private LAN interface).

Common situations: Default config never changed from 127.0.0.1 before trying to pair a phone; laptop on a captive/flagged Wi-Fi that exposes no routable private IP; containers/VMs whose only interfaces are loopback; desiring LAN pairing but hostname left at the safe default.

Related errors


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