tinyhumansai/openhuman · error

[transport:manager] unknown profile kind

Error message

[transport:manager] unknown profile kind: ${kind}

What it means

getTransport() fell through all four known kinds (local, cloud, lan, tunnel) and hit the exhaustive-default throw. ConnectionProfile.kind is the literal union 'local' | 'lan' | 'tunnel' | 'cloud', so at type level this is unreachable — it only happens at runtime with data that bypassed the types.

Solutions

  1. Reset the persisted coreMode/profile state so a default { kind: 'local' } profile is rebuilt
  2. Add a rehydrate guard/migration that maps unknown kinds to 'local' (Redux persist migrate or a transform)
  3. If adding a new kind, extend the ConnectionProfile union and this switch in the same change

Example fix

// before
const kind = persisted.kind; // could be 'remote' from an old build
// after
const KNOWN = ['local', 'lan', 'tunnel', 'cloud'] as const;
const kind = KNOWN.includes(persisted.kind) ? persisted.kind : 'local';
Defensive patterns

Strategy: type-guard

Type guard

const PROFILE_KINDS = ['local', 'lan', 'tunnel', 'cloud'] as const;
type ProfileKind = (typeof PROFILE_KINDS)[number];
function isProfileKind(v: unknown): v is ProfileKind {
  return typeof v === 'string' && (PROFILE_KINDS as readonly string[]).includes(v);
}

Try / catch

try { await manager.getTransport(); }
catch (err) {
  if (err instanceof Error && err.message.includes('unknown profile kind')) {
    dispatch(resetCoreMode()); // rebuild a clean { kind: 'local' } profile
  } else throw err;
}

Prevention

When it happens

Trigger: A persisted profile from a different app version containing a since-removed or since-added kind string (e.g. an old 'remote' kind); corrupted localStorage/persisted Redux data; JSON injected from tests or devtools; a newer backend writing kinds this build does not know.

Common situations: Downgrading the app while keeping persisted state; two products sharing the storage key; hand-edited state during debugging.

Related errors


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

Appendix: source

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

      log('[transport:manager] → CloudHttpTransport rpcUrl=%s', rpcUrl);
      return t;
    }

    if (kind === 'lan') {
      const { rpcUrl } = this.profile;
      if (!rpcUrl) {
        throw new Error('[transport:manager] lan profile missing rpcUrl');
      }
      const t = new LanHttpTransport(rpcUrl);
      log('[transport:manager] → LanHttpTransport rpcUrl=%s', rpcUrl);
      return t;
    }

    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');

View on GitHub (pinned to 7491200858)