tinyhumansai/openhuman · error · Error

[transport:manager] tunnel profile missing sessionToken or p

Error message

[transport:manager] tunnel profile missing sessionToken or pairingToken

What it means

Tunnel authentication needs either an established sessionToken or the one-time pairingToken (sessionToken ?? pairingToken). With neither, TunnelTransport cannot authenticate to the backend socket relay, so the manager aborts before connecting.

Source

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

    throw new Error(`[transport:manager] unknown profile kind: ${kind}`);
  }

  /**
   * Race LAN (with 2 s timeout) against Tunnel.
   * Whichever responds to `openhuman.ping` first wins.
   * If LAN wins but later fails, caller should call reset() to re-race.
   */
  private async raceLanAndTunnel(): Promise<CoreTransport> {
    const { rpcUrl, channelId, corePubkey, sessionToken, pairingToken, devicePrivkey } =
      this.profile;

    if (!channelId || !corePubkey) {
      throw new Error('[transport:manager] tunnel profile missing channelId or corePubkey');
    }

    const tunnelToken = sessionToken ?? pairingToken;
    if (!tunnelToken) {
      throw new Error('[transport:manager] tunnel profile missing sessionToken or pairingToken');
    }

    const tunnelTransport = new TunnelTransport(
      this.backendSocketUrl,
      channelId,
      corePubkey,
      tunnelToken,
      devicePrivkey,
      sessionToken ? 'session' : 'pairing'
    );

    if (!rpcUrl) {
      // No LAN URL — tunnel only.
      log('[transport:manager] → TunnelTransport (no LAN URL)');
      return tunnelTransport;
    }

    const lanTransport = new LanHttpTransport(rpcUrl, LAN_RACE_TIMEOUT_MS);

View on GitHub (pinned to a221052e0d)

Solutions

  1. Re-pair the device to obtain a fresh pairingToken/sessionToken pair
  2. If the sessionToken exists but is rejected later, route through re-auth rather than reusing the profile
  3. Persist the session token atomically with pairing completion so the two cannot diverge

Example fix

// before
const tm = createTransportManager(profile, opts);

// after
if (profile.kind === 'tunnel' && !(profile.sessionToken ?? profile.pairingToken)) {
  await rePairDevice(profile.id); // yields fresh tokens
}
const tm = createTransportManager(profile, opts);
Defensive patterns

Strategy: validation

Validate before calling

function assertTunnelAuthPresent(p: ConnectionProfile): void {
  if (p.kind === 'tunnel' && !(p.sessionToken ?? p.pairingToken)) {
    throw new Error(`profile ${p.id}: no session or pairing token — re-pair the device`);
  }
}

Prevention

When it happens

Trigger: A tunnel profile whose pairingToken was already consumed by a previous successful pairing (they are single-use) and whose sessionToken was never persisted — e.g. the client crashed between server-side pairing completion and storing the session token.

Common situations: Crash between pairing-token exchange and session persistence; session token expired or cleared; restored backup carrying stale tokens.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/32c1f60dd95229ca. Report an issue: GitHub.