{"record":{"id":"dd94586b22045af2","repo":"tinyhumansai/openhuman","slug":"rpc-envelope-contains-undefined-data-dd9458","errorCode":null,"errorMessage":"RPC envelope contains undefined data","messagePattern":"RPC envelope contains undefined data","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/api/threadApi.ts","lineNumber":40,"sourceCode":"  ListTurnStatesResponse,\n  PersistedTurnState,\n  PutTaskBoardResponse,\n  RunEvent,\n  RunEventListResponse,\n  TaskBoard,\n  TaskBoardCard,\n} from '../../types/turnState';\nimport { callCoreRpc } from '../coreRpcClient';\n\ninterface Envelope<T> {\n  data?: T;\n}\n\nfunction unwrapEnvelope<T>(response: Envelope<T> | T): T {\n  if (response && typeof response === 'object' && 'data' in response) {\n    const envelope = response as Envelope<T>;\n    if (envelope.data === undefined) {\n      throw new Error('RPC envelope contains undefined data');\n    }\n    return envelope.data;\n  }\n  return response as T;\n}\n\nconst generateTitleLog = debug('threadApi.generateTitleIfNeeded');\n\nexport const threadApi = {\n  createNewThread: async (labels?: string[]): Promise<Thread> => {\n    const response = await callCoreRpc<Envelope<Thread>>({\n      method: 'openhuman.threads_create_new',\n      params: { labels },\n    });\n    return unwrapEnvelope(response);\n  },\n\n  getThreads: async (): Promise<ThreadsListData> => {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/threadApi.ts#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Reproduce under Vitest and inspect which method's mock resolves {data: undefined} — this error is nearly always test/mock-originated","Fix the mock to either omit the data key entirely or provide a real value: mockResolvedValue(threadFixture), not {data: undefined}","Audit any wrapper on the callCoreRpc path that spreads or rebuilds {data: ...} without checking the source value","Verify against the real core (curl the /rpc method) to confirm the wire shape is fine"],"exampleFix":"// before (test)\nmockRpc.mockResolvedValue({ data: undefined });\n\n// after (test)\nmockRpc.mockResolvedValue(threadFixture); // no envelope at all","handlingStrategy":"type-guard","validationCode":"// In wrappers: only build an envelope when a value actually exists\nreturn value !== undefined ? { data: value } : ({} as Envelope<T>);","typeGuard":"function hasDefinedData<T>(v: unknown): v is { data: T } {\n  return !!v && typeof v === 'object' && 'data' in v && (v as { data: unknown }).data !== undefined;\n}","tryCatchPattern":"try {\n  const thread = await threadApi.createNewThread(labels);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('undefined data')) {\n    // In-memory envelope bug (mock/wrapper) — not a wire failure; fix the producer\n    flagMockOrWrapperBug(e);\n  } else throw e;\n}","preventionTips":["Never mock RPC envelopes as {data: undefined} — omit the key or return the bare value","Remember JSON cannot encode undefined: this error always means JS-constructed data","Type mockResolvedValue against the real response type so TS catches missing fixtures"],"tags":["rpc","envelope","testing","mocking","thread"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}