tinyhumansai/openhuman · error

RPC envelope contains undefined data

Error message

RPC envelope contains undefined data

What it means

Thrown by unwrapEnvelope() in threadApi: the response is an object that HAS a 'data' key, but its value is undefined. Crucially, JSON serialization cannot carry undefined — a real HTTP JSON-RPC body never produces this — so the envelope was constructed in JavaScript, not parsed from the wire. It almost always indicts a mock or an in-process wrapper that adds {data: ...} unconditionally.

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 a221052e0d)

Solutions

  1. Reproduce under Vitest and inspect which method's mock resolves {data: undefined} — this error is nearly always test/mock-originated
  2. Fix the mock to either omit the data key entirely or provide a real value: mockResolvedValue(threadFixture), not {data: undefined}
  3. Audit any wrapper on the callCoreRpc path that spreads or rebuilds {data: ...} without checking the source value
  4. Verify against the real core (curl the /rpc method) to confirm the wire shape is fine

Example fix

// before (test)
mockRpc.mockResolvedValue({ data: undefined });

// after (test)
mockRpc.mockResolvedValue(threadFixture); // no envelope at all
Defensive patterns

Strategy: type-guard

Validate before calling

// In wrappers: only build an envelope when a value actually exists
return value !== undefined ? { data: value } : ({} as Envelope<T>);

Type guard

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

Try / catch

try {
  const thread = await threadApi.createNewThread(labels);
} catch (e) {
  if (e instanceof Error && e.message.includes('undefined data')) {
    // In-memory envelope bug (mock/wrapper) — not a wire failure; fix the producer
    flagMockOrWrapperBug(e);
  } else throw e;
}

Prevention

When it happens

Trigger: A Vitest mock of callCoreRpc resolving {data: undefined} for threads_create_new / other thread methods; a transport or caching wrapper that builds {data: value} even when value is undefined; code doing response = { data: response.data } on a response lacking data.

Common situations: Test suites with partially-specified mocks (the mock author wrote {data: someFixture} where someFixture was undefined); a refactor introducing an envelope-spreading wrapper on the RPC path; a stale service worker / dev shim fabricating responses.

Related errors


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