{"record":{"id":"98cd00b53d901904","repo":"Stirling-Tools/Stirling-PDF","slug":"edge-function-name-returned-no-data","errorCode":null,"errorMessage":"Edge function ${name} returned no data","messagePattern":"Edge function (.+?) returned no data","errorType":"exception","errorClass":"StripeFunctionError","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/portal/billing/stripe.ts","lineNumber":85,"sourceCode":"  name: string,\n  body: Record<string, unknown>,\n): Promise<T> {\n  ensureSaasSupabase();\n  const supabase = getSupabaseClient();\n  if (!supabase) {\n    throw new StripeFunctionError(\n      \"SaaS Supabase not configured — set VITE_SUPABASE_URL.\",\n      \"unconfigured\",\n    );\n  }\n  const { data, error } = await supabase.functions.invoke<T>(name, { body });\n  if (error) {\n    throw new StripeFunctionError(\n      error.message ?? `Edge function ${name} failed`,\n    );\n  }\n  if (data == null) {\n    throw new StripeFunctionError(`Edge function ${name} returned no data`);\n  }\n  return data;\n}\n\n/** Call a SECURITY DEFINER public.* RPC with the admin's JWT (same client as {@link invoke}). */\nasync function rpc<T>(fn: string, args: Record<string, unknown>): Promise<T> {\n  ensureSaasSupabase();\n  const supabase = getSupabaseClient();\n  if (!supabase) {\n    throw new StripeFunctionError(\n      \"SaaS Supabase not configured — set VITE_SUPABASE_URL.\",\n      \"unconfigured\",\n    );\n  }\n  const { data, error } = await supabase.rpc(fn, args);\n  if (error) {\n    throw new StripeFunctionError(\n      error.message ?? `RPC ${fn} failed`,","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/portal/billing/stripe.ts#L67-L103","documentation":"Thrown by invoke() when supabase.functions.invoke resolves without error but returns a null/undefined body. The helper treats a missing payload as a contract violation because every billing edge function is expected to return data.","triggerScenarios":"An edge function returns HTTP 200 with an empty body, returns null explicitly, or responds with a 204 No Content. Because invoke was typed to return T, null data means the typed contract was broken.","commonSituations":"An edge function deploy that forgot to return Response.json(...); a function that returns early on an unhandled branch; a proxy/gateway stripping the body; a successful function whose return was refactored to return null.","solutions":["Check the named edge function source — confirm every code path returns Response.json(...) with a non-null body.","Redeploy the function after fixing the empty-return branch.","Confirm the Supabase project + function slug in VITE_SUPABASE_URL point at the correct deployment.","Add server-side logging in the function so empty returns are traceable."],"exampleFix":"// edge function (Supabase) — before\nreturn new Response(null, { status: 200 });\n\n// after\nreturn new Response(JSON.stringify({ success: true, ...result }), {\n  headers: { \"Content-Type\": \"application/json\" },\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function hasPayload<T>(data: T | null | undefined): data is T {\n  return data != null;\n}","tryCatchPattern":"try {\n  const res = await invoke<CheckoutResponse>(\"create-checkout-session\", body);\n  // invoke already throws on null data, so reaching here means data is present\n  useCheckout(res);\n} catch (e) {\n  if (e instanceof StripeFunctionError) toast(e.message);\n}","preventionTips":["Ensure every edge function code path returns Response.json with a non-null body.","Add a post-deploy smoke test that calls each function and asserts a non-empty body.","Log empty-body responses server-side so they are caught before users hit them."],"tags":["billing","stripe","supabase","edge-function","contract"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}