Stirling-Tools/Stirling-PDF · error · StripeFunctionError

Edge function ${name} returned no data

Error message

Edge function ${name} returned no data

What it means

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.

Source

Thrown at frontend/editor/src/portal/billing/stripe.ts:85

  name: string,
  body: Record<string, unknown>,
): Promise<T> {
  ensureSaasSupabase();
  const supabase = getSupabaseClient();
  if (!supabase) {
    throw new StripeFunctionError(
      "SaaS Supabase not configured — set VITE_SUPABASE_URL.",
      "unconfigured",
    );
  }
  const { data, error } = await supabase.functions.invoke<T>(name, { body });
  if (error) {
    throw new StripeFunctionError(
      error.message ?? `Edge function ${name} failed`,
    );
  }
  if (data == null) {
    throw new StripeFunctionError(`Edge function ${name} returned no data`);
  }
  return data;
}

/** Call a SECURITY DEFINER public.* RPC with the admin's JWT (same client as {@link invoke}). */
async function rpc<T>(fn: string, args: Record<string, unknown>): Promise<T> {
  ensureSaasSupabase();
  const supabase = getSupabaseClient();
  if (!supabase) {
    throw new StripeFunctionError(
      "SaaS Supabase not configured — set VITE_SUPABASE_URL.",
      "unconfigured",
    );
  }
  const { data, error } = await supabase.rpc(fn, args);
  if (error) {
    throw new StripeFunctionError(
      error.message ?? `RPC ${fn} failed`,

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Check the named edge function source — confirm every code path returns Response.json(...) with a non-null body.
  2. Redeploy the function after fixing the empty-return branch.
  3. Confirm the Supabase project + function slug in VITE_SUPABASE_URL point at the correct deployment.
  4. Add server-side logging in the function so empty returns are traceable.

Example fix

// edge function (Supabase) — before
return new Response(null, { status: 200 });

// after
return new Response(JSON.stringify({ success: true, ...result }), {
  headers: { "Content-Type": "application/json" },
});
Defensive patterns

Strategy: try-catch

Type guard

function hasPayload<T>(data: T | null | undefined): data is T {
  return data != null;
}

Try / catch

try {
  const res = await invoke<CheckoutResponse>("create-checkout-session", body);
  // invoke already throws on null data, so reaching here means data is present
  useCheckout(res);
} catch (e) {
  if (e instanceof StripeFunctionError) toast(e.message);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13). Data as JSON: /api/errors/98cd00b53d901904. Report an issue: GitHub.