{"record":{"id":"dd94586b22045af2","repo":"tinyhumansai/openhuman","slug":"rpc-envelope-contains-undefined-data","errorCode":null,"errorMessage":"RPC envelope contains undefined data","messagePattern":"RPC envelope contains undefined data","errorType":"exception","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/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/services/api/threadApi.ts#L22-L58","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Restart/update the core so threads handlers populate data.","Check the specific RPC method that failed (the throw is generic) in core logs.","Fix call sites that re-wrap responses in an extra {data: ...} layer."],"exampleFix":"// before\nif (envelope.data === undefined) {\n  throw new Error('RPC envelope contains undefined data');\n}\nreturn envelope.data;\n\n// after — include method context and tolerate null for optional reads\nif (envelope.data === undefined) {\n  throw new Error(`RPC envelope contains undefined data (method: ${method})`);\n}\nreturn envelope.data;\n// for optional reads, prefer: return envelope.data ?? null;","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isEnvelopeWithDefinedData<T>(v: unknown): v is { data: T } {\n  return Boolean(v) && typeof v === 'object' && 'data' in (v as object)\n    && (v as { data: unknown }).data !== undefined;\n}","tryCatchPattern":"try {\n  return unwrapEnvelope<Thread>(response);\n} catch (e) {\n  if (e instanceof Error && e.message === 'RPC envelope contains undefined data') {\n    // core emitted an empty envelope — restart/update core; do not retry blindly\n    throw new Error('Thread data unavailable (empty RPC envelope) — restart Core.');\n  }\n  throw e;\n}","preventionTips":["Never re-wrap a response in an extra {data: ...} layer at call sites.","Log the failing RPC method alongside this error for triage.","Keep handler return shapes ({data: T}) in lockstep with unwrapEnvelope."],"tags":["core-rpc","envelope-unwrapping","threads","response-validation"],"backgroundTag":"empty-rpc-response","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}