{"record":{"id":"585711770c291980","repo":"tinyhumansai/openhuman","slug":"provider-surfaces-list-queue-unexpected-empty-res","errorCode":null,"errorMessage":"provider_surfaces_list_queue: unexpected empty response","messagePattern":"provider_surfaces_list_queue: unexpected empty response","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/api/providerSurfacesApi.ts","lineNumber":13,"sourceCode":"import type { RespondQueueList } from '../../types/providerSurfaces';\nimport { callCoreRpc } from '../coreRpcClient';\n\ninterface ProviderSurfacesQueueEnvelope {\n  data?: RespondQueueList;\n  result?: { data?: RespondQueueList };\n}\n\nconst EMPTY_QUEUE: RespondQueueList = { items: [], count: 0 };\n\nfunction parseQueueEnvelope(raw: unknown): RespondQueueList {\n  if (!raw || typeof raw !== 'object') {\n    throw new Error('provider_surfaces_list_queue: unexpected empty response');\n  }\n\n  const envelope = raw as ProviderSurfacesQueueEnvelope & { error?: { message?: string } };\n  if (envelope.error) {\n    throw new Error(envelope.error.message ?? 'Core RPC returned an error');\n  }\n  const candidate = envelope.result?.data ?? envelope.data;\n  if (!candidate || !Array.isArray(candidate.items) || typeof candidate.count !== 'number') {\n    return EMPTY_QUEUE;\n  }\n  return candidate;\n}\n\nexport const providerSurfacesApi = {\n  async listQueue(): Promise<RespondQueueList> {\n    const raw = await callCoreRpc<unknown>({ method: 'openhuman.provider_surfaces_list_queue' });\n    return parseQueueEnvelope(raw);\n  },","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/providerSurfacesApi.ts#L1-L31","documentation":"Thrown by parseQueueEnvelope() in providerSurfacesApi when the raw value returned by 'openhuman.provider_surfaces_list_queue' is falsy or not an object (undefined, null, string, number). Note the asymmetry: an object with a malformed/missing items payload is tolerated and returned as EMPTY_QUEUE — this error fires only when there is no object at all to parse.","triggerScenarios":"callCoreRpc resolves with undefined (a mocked client in tests returning nothing, or a wrapper that reads a missing key), or the JSON-RPC result is literally null (a core controller returning Option::None serialized as null).","commonSituations":"Core version predating the provider_surfaces controller while the frontend ships it; a Vitest mock of callCoreRpc that returns undefined instead of a payload; the method returning null before the respond-queue store initializes.","solutions":["Confirm the core registers the method: GET /schema must list provider_surfaces_list_queue","Update the desktop app and core together so the provider-surfaces surface exists on both sides","If writing tests, make the callCoreRpc mock resolve {data: {items: [], count: 0}} or {result: {data: ...}} instead of undefined","If developing the Rust controller, never return a bare null result — return {items: [], count: 0}"],"exampleFix":"// test/mock: before\nvi.mocked(callCoreRpc).mockResolvedValue(undefined as any);\n\n// after\nvi.mocked(callCoreRpc).mockResolvedValue({ data: { items: [], count: 0 } });","handlingStrategy":"fallback","validationCode":"// Probe with the raw RPC before relying on the helper:\nconst raw = await callCoreRpc<unknown>({ method: 'openhuman.provider_surfaces_list_queue' });\nif (!raw || typeof raw !== 'object') {\n  useEmptyQueue(); // core predates the surface — do not call providerSurfacesApi.listQueue\n}","typeGuard":"function isQueuePayload(v: unknown): v is { items: unknown[]; count: number } {\n  const r = v as Record<string, unknown> | null | undefined;\n  const cand = (r?.result as Record<string, unknown> | undefined)?.data ?? r?.data;\n  return !!cand && Array.isArray((cand as any).items) && typeof (cand as any).count === 'number';\n}","tryCatchPattern":"try {\n  const queue = await providerSurfacesApi.listQueue();\n  renderQueue(queue);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('unexpected empty response')) {\n    renderQueue({ items: [], count: 0 }); // degrade gracefully — queue view is non-critical\n  } else throw e;\n}","preventionTips":["Mock callCoreRpc with a concrete object payload in tests, never undefined","Check GET /schema when adding newly-shipped RPC consumers","Return {items: [], count: 0} from new core controllers instead of null results"],"tags":["rpc","validation","provider-surfaces","queue","mocking"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}