tinyhumansai/openhuman · error

[transport:lan] ${method} timed out after ${this.timeoutMs}m

Error message

[transport:lan] ${method} timed out after ${this.timeoutMs}ms

What it means

LAN-transport timeout: `fetch` rejected and the shared controller was aborted — by the internal timer (default 10s, deliberately shorter than cloud/local) or by the caller's signal, which the `controller.signal.aborted` check cannot distinguish. This transport connects a remote client (e.g. the iOS app) to the desktop core over the local network with no Authorization header (network-level trust per the class docs), so a timeout almost always means the desktop core was unreachable or slow.

Source

Thrown at app/src/services/transport/LanHttpTransport.ts:60

    const payload: JsonRpcRequestBody = { jsonrpc: '2.0', id, method, params: params ?? {} };

    log('[transport:lan] → %s id=%d', method, id);

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
    opts?.signal?.addEventListener('abort', () => controller.abort());

    let response: Response;
    try {
      response = await fetch(this.rpcUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
        signal: controller.signal,
      });
    } catch (err) {
      if (controller.signal.aborted) {
        throw new Error(`[transport:lan] ${method} timed out after ${this.timeoutMs}ms`);
      }
      throw err;
    } finally {
      clearTimeout(timeoutId);
    }

    if (!response.ok) {
      const text = await response.text();
      throw new Error(`[transport:lan] HTTP ${response.status}: ${text || response.statusText}`);
    }

    const json = (await response.json()) as JsonRpcResponse<T>;

    if (json.error) {
      logErr('[transport:lan] ← %s error: %s', method, json.error.message);
      throw new Error(json.error.message ?? 'LAN RPC returned an error');
    }
    if (!Object.prototype.hasOwnProperty.call(json, 'result')) {

View on GitHub (pinned to a221052e0d)

Solutions

  1. Verify both devices are on the same network and the desktop core is running (its URL answers `GET /health`)
  2. Re-pair/re-fetch the connection profile so rpcUrl matches the desktop's current IP:port
  3. Allow the port through the desktop firewall; ensure the core's listener posture covers LAN (a deliberate user decision, not default)
  4. Raise `timeoutMs` in the LanHttpTransport constructor for known-slow calls, and retry idempotent ones with backoff

Example fix

// before
const t = new LanHttpTransport(profile.rpcUrl); // 10s default
await t.call('openhuman.memory_search', q);

// after
const t = new LanHttpTransport(profile.rpcUrl, 30_000);
await withRetry(() => t.call('openhuman.memory_search', q), { tries: 3 });
Defensive patterns

Strategy: retry

Validate before calling

async function lanReachable(rpcUrl: string): Promise<boolean> {
  try {
    const r = await fetch(new URL('/health', rpcUrl).toString(), { signal: AbortSignal.timeout(3000) });
    return r.ok;
  } catch { return false; }
}

Type guard

function isLanTimeout(e: unknown): boolean {
  return e instanceof Error && e.message.startsWith('[transport:lan]') && e.message.includes('timed out');
}

Try / catch

if (!(await lanReachable(profile.rpcUrl))) {
  throw new Error('Desktop core unreachable — check both devices share the network.');
}
for (let a = 1; a <= 3; a++) {
  try { return await lan.call(m, p); }
  catch (e) { if (!isLanTimeout(e) || a === 3) throw e; await sleep(2 ** a * 300); }
}

Prevention

When it happens

Trigger: iOS device and desktop on different networks/VLANs; desktop core not running or its listener bound to loopback only; phone Wi-Fi with poor signal to the desktop host; the 10s default being too tight for a heavy method over Wi-Fi; caller cancellation mislabeled as timeout.

Common situations: Connection profile's rpcUrl host/port stale after the desktop's IP changed (DHCP); firewall on the desktop blocking the port; desktop asleep; user switched from home to public Wi-Fi which isolates clients.

Understand the failure class

Related errors


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