tinyhumansai/openhuman · error

RPC envelope contains undefined data

Error message

RPC envelope contains undefined data

What it means

Thrown by unwrapEnvelope() in threadApi when the RPC response is an object that has a `data` key but its value is undefined. The convention is that list/get handlers return {data: T}; an envelope whose data key exists yet holds undefined means the core built an envelope without content — distinct from a bare payload (returned as-is) and from a null response (also passed through, since 'data' in response is false for null). This guards every thread API call routed through unwrapEnvelope.

Solutions

  1. Log the full response object at the call site to see whether data is null-but-present (core emitted an empty envelope) vs the payload riding a different key.
  2. Restart/update the core so threads handlers populate data.
  3. Check the specific RPC method that failed (the throw is generic) in core logs.
  4. Fix call sites that re-wrap responses in an extra {data: ...} layer.

Example fix

// before
if (envelope.data === undefined) {
  throw new Error('RPC envelope contains undefined data');
}
return envelope.data;

// after — include method context and tolerate null for optional reads
if (envelope.data === undefined) {
  throw new Error(`RPC envelope contains undefined data (method: ${method})`);
}
return envelope.data;
// for optional reads, prefer: return envelope.data ?? null;
Defensive patterns

Strategy: type-guard

Type guard

function isEnvelopeWithDefinedData<T>(v: unknown): v is { data: T } {
  return Boolean(v) && typeof v === 'object' && 'data' in (v as object)
    && (v as { data: unknown }).data !== undefined;
}

Try / catch

try {
  return unwrapEnvelope<Thread>(response);
} catch (e) {
  if (e instanceof Error && e.message === 'RPC envelope contains undefined data') {
    // core emitted an empty envelope — restart/update core; do not retry blindly
    throw new Error('Thread data unavailable (empty RPC envelope) — restart Core.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Core handler returns Ok(json!({"data": null})) or serializes a struct with data: Option::None; TypeScript caller re-wraps a result ({data: response.data}) where response.data was already missing; version skew where a handler's data field was renamed.

Common situations: Creating/fetching threads against an older core; a handler edge case (e.g. thread not found) that still emits the envelope skeleton; tests double-wrapping envelopes.

Related errors


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

Appendix: source

Thrown at app/src/services/api/threadApi.ts:40

  ListTurnStatesResponse,
  PersistedTurnState,
  PutTaskBoardResponse,
  RunEvent,
  RunEventListResponse,
  TaskBoard,
  TaskBoardCard,
} from '../../types/turnState';
import { callCoreRpc } from '../coreRpcClient';

interface Envelope<T> {
  data?: T;
}

function unwrapEnvelope<T>(response: Envelope<T> | T): T {
  if (response && typeof response === 'object' && 'data' in response) {
    const envelope = response as Envelope<T>;
    if (envelope.data === undefined) {
      throw new Error('RPC envelope contains undefined data');
    }
    return envelope.data;
  }
  return response as T;
}

const generateTitleLog = debug('threadApi.generateTitleIfNeeded');

export const threadApi = {
  createNewThread: async (labels?: string[]): Promise<Thread> => {
    const response = await callCoreRpc<Envelope<Thread>>({
      method: 'openhuman.threads_create_new',
      params: { labels },
    });
    return unwrapEnvelope(response);
  },

  getThreads: async (): Promise<ThreadsListData> => {

View on GitHub (pinned to 7491200858)