{"record":{"id":"fc695726be84fa96","repo":"moeru-ai/airi","slug":"no-reachable-private-lan-address-is-available-for","errorCode":null,"errorMessage":"No reachable private LAN address is available for the current server channel host.","messagePattern":"No reachable private LAN address is available for the current server channel host\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/main/services/airi/channel-server/index.ts","lineNumber":114,"sourceCode":"  }\n\n  return [config.hostname]\n}\n\nfunction createServerChannelUrl(protocol: 'ws' | 'wss', host: string) {\n  const urlHost = isIP(host) === 6 ? `[${host}]` : host\n  // TODO: Deduplicate the server channel websocket path with `packages/server-runtime/src/index.ts`\n  // and `packages/server-sdk/src/client.ts` so this does not rely on three separate `/ws` literals.\n  return `${protocol}://${urlHost}:${getServerChannelPort()}/ws`\n}\n\nfunction getServerChannelQrPayload(config: ElectronServerChannelConfig, serverChannel: Server) {\n  const protocol = config.tlsConfig ? 'wss' : 'ws'\n  const urls = getServerChannelQrHosts(config, serverChannel)\n    .map(host => createServerChannelUrl(protocol, host))\n\n  if (!urls.length) {\n    throw new Error('No reachable private LAN address is available for the current server channel host.')\n  }\n\n  return createServerChannelQrPayload({\n    type: 'airi:server-channel',\n    version: 1,\n    urls,\n    authToken: config.authToken,\n  })\n}\n\nasync function getChannelServerConfig(): Promise<ElectronServerChannelConfig> {\n  const config = channelServerConfigStore.get() || { hostname: '127.0.0.1', authToken: '', tlsConfig: null }\n\n  return {\n    hostname: config.hostname || '127.0.0.1',\n    authToken: config.authToken || '',\n    tlsConfig: config.tlsConfig || null,\n  }","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/main/services/airi/channel-server/index.ts#L96-L132","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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.","Or set the hostname explicitly to the machine's LAN IP (e.g. 192.168.x.x).","Confirm the network interface is up and not VPN-only before requesting the QR payload.","If pairing is unnecessary on this host, avoid calling electronGetServerChannelQrPayload."],"exampleFix":"// before: hostname '127.0.0.1' -> getServerChannelQrHosts returns []\n\n// after\nawait applyServerChannelConfig({ hostname: '0.0.0.0' })\n// now non-loopback LAN addresses are advertised in the QR","handlingStrategy":"validation","validationCode":"function hasLanAddress(config, serverChannel): boolean {\n  return getServerChannelQrHosts(config, serverChannel).length > 0\n}\n\nif (!hasLanAddress(config, serverChannel)) {\n  // prompt user to set hostname '0.0.0.0' or a LAN IP before requesting the QR\n}","typeGuard":"function isNonLoopbackHost(host: string): boolean {\n  return !['localhost', '127.0.0.1', '::1'].includes(host)\n}","tryCatchPattern":null,"preventionTips":["Set the server-channel hostname to '0.0.0.0' or a LAN IP before pairing.","Verify a non-loopback interface exists before requesting the QR payload.","Avoid requesting the QR on loopback-only networks."],"tags":["server-channel","network","configuration","electron","qr","lan"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}