tinyhumansai/openhuman · error · CoreRpcError

Core RPC returned an error

Error message

Core RPC returned an error

What it means

Fallback message used when the embedded Rust core answers a JSON-RPC call with an `error` object whose `message` field is missing or empty (`json.error.message || 'Core RPC returned an error'`). The actual failure happened core-side in a controller handler; this string only stands in for an empty message. The thrown value is a `CoreRpcError` that also carries `kind` (from `classifyRpcError(rawMessage, undefined, json.error.data)`) and the raw `json.error.data`, which usually contain the real diagnosis even when the message is blank.

Source

Thrown at app/src/services/coreRpcClient.ts:760

          payload.method,
          classifyAuthExpiredReason(text || response.statusText, response.status)
        );
      throw new CoreRpcError(httpMessage, kind, response.status);
    }

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

    if (json.error) {
      coreRpcError('HTTP error response', {
        id: payload.id,
        method: payload.method,
        error: json.error,
      });
      const rawMessage = json.error.message || 'Core RPC returned an error';
      const kind = classifyRpcError(rawMessage, undefined, json.error.data);
      if (kind === 'auth_expired' && !suppressAuthExpiredEvent)
        dispatchAuthExpired(payload.method, classifyAuthExpiredReason(rawMessage, undefined));
      throw new CoreRpcError(rawMessage, kind, undefined, json.error.data);
    }
    if (!Object.prototype.hasOwnProperty.call(json, 'result')) {
      throw new Error('Core RPC response missing result');
    }

    coreRpcLog('HTTP response', { id: payload.id, method: payload.method });
    return json.result as T;
  } catch (err) {
    coreRpcError('Core RPC call failed', sanitizeError(err));
    if (err instanceof CoreRpcError) throw err;
    const message = coreRpcErrorMessage(err);
    throw new CoreRpcError(message, classifyRpcError(message));
  }
}

View on GitHub (pinned to a221052e0d)

Solutions

  1. Inspect the caught `CoreRpcError`'s `kind` and `data` fields — the message can be empty while `data` holds a typed kind (e.g. `{ kind: 'ThreadNotFound' }`)
  2. Enable the debug logger (`DEBUG=core-rpc:error` or `debug.enable('core-rpc:error')`) and reproduce — the full `json.error` object is logged with id/method before the throw
  3. Check the core-side log file for the same timestamp; the Rust handler usually logs the underlying cause
  4. Rebuild/restart the core (`pnpm dev:app` or `restart_core_process`) so the controller set matches the frontend expectations

Example fix

// before
try { await coreRpcClient.call('openhuman.memory_search', params); }
catch (e) { toast(String(e)); }

// after
import { CoreRpcError } from '../services/coreRpcClient';
try { await coreRpcClient.call('openhuman.memory_search', params); }
catch (e) {
  if (e instanceof CoreRpcError) {
    // data carries the typed reason even when message is the empty fallback
    toast(e.data ? JSON.stringify(e.data) : e.message, e.kind);
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Type guard

import { CoreRpcError } from '../services/coreRpcClient';

function isCoreRpcError(e: unknown): e is CoreRpcError {
  return e instanceof CoreRpcError;
}

Try / catch

try {
  const r = await coreRpcClient.call('openhuman.x_y', params);
} catch (e) {
  if (e instanceof CoreRpcError) {
    // message may be the empty fallback — prefer data/kind
    report({ kind: e.kind, data: e.data, message: e.message });
    if (e.kind === 'auth_expired') reAuth();
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Any `coreRpcClient.call` / `callCoreRpc` invocation where the core replies HTTP 200 with `{ "error": { "code": N, "message": "" } }` or an error object with no `message` key — e.g. a Rust controller returning an error struct whose message serializes to null, or a serde error mapped to a bare code. The code path first logs the full `json.error` at `core-rpc:error` as 'HTTP error response' with the request id and method.

Common situations: Frontend/core version skew (stale core binary lacking a new method), session expiry where the reason rides in `error.data.kind`, controllers that return `Err` with a unit or empty-string message, auth rejection from the bearer-token gate on `/rpc`.

Related errors


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