tinyhumansai/openhuman · critical

[transport:manager] all transports failed to connect

Error message

[transport:manager] all transports failed to connect

What it means

raceLanAndTunnel() first races LAN isHealthy (2s LAN_RACE_TIMEOUT_MS) against tunnel isHealthy; when both resolve null it awaits Promise.any for a late winner, and if that also yields null it gives up: neither the LAN HTTP ping nor the tunnel handshake ever succeeded. This is the terminal 'no path to the core' error for tunnel profiles.

Solutions

  1. Call manager.reset() and retry once — the doc comment explicitly designs reset() for re-racing after a winner later failed
  2. Verify the desktop core is running and on the same network, then retry while on LAN
  3. Check tunnel credentials (re-pair if the session was revoked) via the error surfaced by the losing tunnelRace
  4. Fall back to another profile (local on the desktop itself, or cloud) until connectivity returns

Example fix

// before
const t = await manager.getTransport();
// after
let t;
try {
  t = await manager.getTransport();
} catch (err) {
  manager.reset();
  t = await manager.getTransport(); // re-race LAN vs Tunnel once
}
Defensive patterns

Strategy: retry

Validate before calling

// Cheap reachability split before the expensive race:
async function lanReachable(rpcUrl: string): Promise<boolean> {
  try { await fetch(rpcUrl, { method: 'HEAD' }); return true; } catch { return false; }
}

Try / catch

try {
  return await manager.getTransport();
} catch (err) {
  if (err instanceof Error && err.message.includes('all transports failed')) {
    manager.reset();            // documented recovery: re-race LAN vs Tunnel
    await sleep(2_000);
    return await manager.getTransport();
  }
  throw err;
}

Prevention

When it happens

Trigger: Desktop host unreachable on LAN (asleep, off, IP changed) AND the backend relay rejecting the tunnel (invalid/expired sessionToken, channel closed) or the relay itself down; both transports' openhuman.ping health checks failing.

Common situations: Remote usage where the home machine is off; device session revoked while also off-LAN; backend outage; firewall blocking both the LAN port and websocket egress.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/cae4e3af1590aefd. Report an issue: GitHub.

Appendix: source

Thrown at app/src/services/transport/TransportManager.ts:174

    const winner = await Promise.race([lanRace, tunnelRace]);

    if (winner) {
      // Close the losing transport.
      void winner.loser.close().catch(() => {});
      log('[transport:manager] race winner: %s', winner.transport.kind);
      return winner.transport;
    }

    // Both failed in the race window — wait for whichever succeeds.
    logErr('[transport:manager] race: both transports unhealthy; waiting…');
    const result = await Promise.any([lanRace, tunnelRace]);
    if (result) {
      void result.loser.close().catch(() => {});
      log('[transport:manager] fallback winner: %s', result.transport.kind);
      return result.transport;
    }

    throw new Error('[transport:manager] all transports failed to connect');
  }
}

// -- convenience factory ------------------------------------------------------

/**
 * Build a TransportManager from a ConnectionProfile.
 * `localRpcUrl` / `localToken` are only needed for kind="local".
 */
export function createTransportManager(
  profile: ConnectionProfile,
  opts: {
    localRpcUrl?: () => Promise<string>;
    localToken?: () => Promise<string | null>;
    backendSocketUrl?: string;
  } = {}
): TransportManager {
  const noop = () => Promise.resolve(null);

View on GitHub (pinned to 7491200858)