Stirling-Tools/Stirling-PDF · error · StripeFunctionError

RPC ${fn} failed

Error message

RPC ${fn} failed

What it means

Fallback message in rpc() when a PostgREST/supabase.rpc call rejects with an error that has no .message. The helper still forwards the error's code when present (the cast `(error as { code?: string }).code`), but the human text falls back to naming the failed function.

Source

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

  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`,
      (error as { code?: string }).code,
    );
  }
  return data as T;
}

/** Inputs to {@link upsertBundleQuote} — the sized config + computed figures. */
export interface BundleQuoteInput {
  teamId: number;
  users: number;
  posturePolicies: number;
  sizeMult: number;
  pipelineMult: number;
  provisionedMonthlyVolume: number;
  /** Size-folded run-credits = the Stripe line quantity when this quote is paid. */
  poolCredits: number;
  /**

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Read the PostgREST error code/hint from the StripeFunctionError.code field and the underlying error in dev tools.
  2. Check the SQL function for the named fn in the database — look at EXCEPTION blocks and the SECURITY DEFINER grant.
  3. Validate the RPC args (team_id, quote_id, etc.) before calling rpc().
  4. Keep @supabase/supabase-js on the version the code expects to avoid error-shape drift.

Example fix

// before
throw new StripeFunctionError(
  error.message ?? `RPC ${fn} failed`,
  (error as { code?: string }).code,
);

// after — include hint/code for faster triage
const e = error as { code?: string; hint?: string };
throw new StripeFunctionError(
  error.message ?? `RPC ${fn} failed (code=${e.code ?? "?"}${e.hint ? ": " + e.hint : ""})`,
  e.code,
);
Defensive patterns

Strategy: try-catch

Type guard

function isPostgresError(e: unknown): e is { message?: string; code?: string; hint?: string } {
  return typeof e === "object" && e !== null && ("code" in e || "message" in e);
}

Try / catch

try {
  await upsertBundleQuote(input);
} catch (e) {
  if (e instanceof StripeFunctionError) {
    // e.code carries the PostgREST/SQL code when available
    notifications.show({ message: `Quote failed (${e.code ?? "unknown"}): ${e.message}`, color: "red" });
  } else throw e;
}

Prevention

When it happens

Trigger: supabase.rpc(fn, args) rejects with an error object lacking message (or message === ''), e.g. a PostgREST error with only a code/hint, or a transport error surfaced without text.

Common situations: A SECURITY DEFINER function raised an exception with a code but no message; a PostgREST-level error (4xx on the RPC) whose shape changed across supabase-js versions; a network failure mid-RPC.

Related errors


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