tinyhumansai/openhuman · error

Cloud RPC returned an error

Error message

Cloud RPC returned an error

What it means

The cloud core answered 2xx but with a JSON-RPC error envelope whose message was null/undefined — the log line ('[transport:cloud] ← %s error') still captures json.error.message before this generic fallback is thrown. This is the core/application rejecting the method call (unknown method, invalid params, domain error) with an unhelpfully empty message.

Solutions

  1. Enable DEBUG='transport:cloud:error' — the logErr line prints json.error.message and lets you correlate the code (json.error.code) with the cloud core's error table
  2. Confirm the method exists on the cloud core (its /schema endpoint) before calling
  3. Align app and cloud core versions; gate new-method calls on a capability check
  4. Report empty-message errors upstream — the handler should always fill json.error.message

Example fix

// before
const r = await transport.call<T>('openhuman.meet_list_upcoming', {});

// after
import debug from 'debug';
debug.enable('transport:cloud:error'); // surface json.error.code/message from the logErr line
const r = await transport.call<T>('openhuman.meet_list_upcoming', {});
Defensive patterns

Strategy: try-catch

Type guard

function isCloudRpcEnvelopeError(err: unknown): boolean {
  return err instanceof Error && (err.message === 'Cloud RPC returned an error' || err.message.startsWith('[transport:cloud]'));
}

Try / catch

try {
  return await transport.call<T>(method, params);
} catch (err) {
  if (isCloudRpcEnvelopeError(err)) {
    // enable DEBUG='transport:cloud:error': json.error.code/message were logged pre-throw
    logger.error('cloud core rejected call', { method });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a method the cloud core doesn't register (version skew between app and cloud core); params failing server-side validation where the handler returns an error without a message; cloud-side domain failures that omit message fields.

Common situations: App build newer than the deployed cloud core; DomainSet/feature differences on the remote core; API contract drift after a cloud update.

Related errors


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

Appendix: source

Thrown at app/src/services/transport/CloudHttpTransport.ts:82

    } catch (err) {
      if (controller.signal.aborted) {
        throw new Error(`[transport:cloud] ${method} timed out after ${this.timeoutMs}ms`);
      }
      throw err;
    } finally {
      clearTimeout(timeoutId);
    }

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

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

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

    log('[transport:cloud] ← %s id=%d ok', method, id);
    return json.result as T;
  }

  async *stream<T>(
    method: string,
    params: unknown,
    opts?: { signal?: AbortSignal }
  ): AsyncIterable<T> {
    const result = await this.call<T>(method, params, opts);
    yield result;
  }

View on GitHub (pinned to 7491200858)