tinyhumansai/openhuman · error · Error

[transport:manager] tunnel profile missing channelId or core

Error message

[transport:manager] tunnel profile missing channelId or corePubkey

What it means

A kind 'tunnel' profile must identify the relay channel (channelId) and the core's public key (corePubkey) so TunnelTransport can route and encrypt; missing either makes the tunnel unusable, so raceLanAndTunnel refuses before racing LAN against tunnel.

Source

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

    if (kind === 'tunnel') {
      return this.raceLanAndTunnel();
    }

    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.

View on GitHub (pinned to a221052e0d)

Solutions

  1. Re-run device pairing so both channelId and corePubkey are recorded
  2. Delete the half-paired device entry and pair again
  3. Add a load-time completeness assertion (see guard) that fails with the profile id

Example fix

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

// after
if (profile.kind === 'tunnel' && (!profile.channelId || !profile.corePubkey)) {
  throw new Error(`profile ${profile.id}: tunnel pairing incomplete — re-pair the device`);
}
const tm = createTransportManager(profile, opts);
Defensive patterns

Strategy: validation

Validate before calling

function assertTunnelProfilePaired(p: ConnectionProfile): void {
  if (p.kind === 'tunnel' && (!p.channelId || !p.corePubkey)) {
    throw new Error(`profile ${p.id}: tunnel pairing incomplete (channelId/corePubkey missing) — re-pair`);
  }
}

Prevention

When it happens

Trigger: getTransport() on a tunnel profile saved before pairing completed — the flow was interrupted after channel creation but before the core pubkey exchange, so one of the two fields is empty.

Common situations: Interrupted device pairing (security/devices domain); partial profile persistence from a crash mid-write; test fixtures missing tunnel fields.

Related errors


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