{"record":{"id":"308f72eeecc40108","repo":"tinyhumansai/openhuman","slug":"transport-manager-lan-profile-missing-rpcurl","errorCode":null,"errorMessage":"[transport:manager] lan profile missing rpcUrl","messagePattern":"\\[transport:manager\\] lan profile missing rpcUrl","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"app/src/services/transport/TransportManager.ts","lineNumber":90,"sourceCode":"      const t = new LocalTransport(this.localRpcUrl, this.localToken);\n      log('[transport:manager] → LocalTransport');\n      return t;\n    }\n\n    if (kind === 'cloud') {\n      const { rpcUrl, sessionToken } = this.profile;\n      if (!rpcUrl) {\n        throw new Error('[transport:manager] cloud profile missing rpcUrl');\n      }\n      const t = new CloudHttpTransport(rpcUrl, sessionToken ?? null);\n      log('[transport:manager] → CloudHttpTransport rpcUrl=%s', rpcUrl);\n      return t;\n    }\n\n    if (kind === 'lan') {\n      const { rpcUrl } = this.profile;\n      if (!rpcUrl) {\n        throw new Error('[transport:manager] lan profile missing rpcUrl');\n      }\n      const t = new LanHttpTransport(rpcUrl);\n      log('[transport:manager] → LanHttpTransport rpcUrl=%s', rpcUrl);\n      return t;\n    }\n\n    if (kind === 'tunnel') {\n      return this.raceLanAndTunnel();\n    }\n\n    throw new Error(`[transport:manager] unknown profile kind: ${kind}`);\n  }\n\n  /**\n   * Race LAN (with 2 s timeout) against Tunnel.\n   * Whichever responds to `openhuman.ping` first wins.\n   * If LAN wins but later fails, caller should call reset() to re-race.\n   */","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/transport/TransportManager.ts#L72-L108","documentation":"The selected ConnectionProfile has kind 'lan' but no rpcUrl, so TransportManager cannot construct the LanHttpTransport. LAN profiles must carry the desktop core's LAN address (host:port of its RPC listener); nothing is dialed before this throw.","triggerScenarios":"manager.getTransport() with { kind: 'lan', rpcUrl: undefined } — typically a profile recorded before LAN discovery filled the address, or a deserialized profile from an older schema (e.g. iOS client pairing that did not complete discovery).","commonSituations":"Mobile/remote client pairing interrupted before the core's LAN address was captured; persisted profile from before a schema change; hand-built profile fixtures in tests.","solutions":["Re-run LAN discovery / re-pair so the profile records the core's LAN rpcUrl","Verify the desktop core actually advertises a LAN address and both devices share the network","Validate profiles at load time and rebuild incomplete ones instead of failing at first RPC"],"exampleFix":"// before\nconst tm = createTransportManager(profile, opts);\n\n// after\nif (profile.kind === 'lan' && !profile.rpcUrl) {\n  await reDiscoverLan(profile.id); // refresh address, then rebuild\n}\nconst tm = createTransportManager(profile, opts);","handlingStrategy":"validation","validationCode":"function assertProfileComplete(p: ConnectionProfile): void {\n  if ((p.kind === 'cloud' || p.kind === 'lan') && !p.rpcUrl) {\n    throw new Error(`profile ${p.id} (${p.kind}) is missing rpcUrl — re-run discovery/pairing`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat a LAN profile without rpcUrl as incomplete pairing — re-run LAN discovery before use","Check the desktop core advertises its LAN address on the shared network","Validate profiles at load and rebuild incomplete ones"],"tags":["transport","lan","connection-profile","discovery","config"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}