Stirling-Tools/Stirling-PDF · error · StripeFunctionError

unconfigured

unconfigured

Error message

SaaS Supabase not configured — set VITE_SUPABASE_URL.

What it means

Thrown by the invoke() helper used by all Stripe edge-function calls. ensureSaasSupabase() runs first (asserting SaaS mode), then getSupabaseClient() is expected to return a configured client. If it returns null — meaning VITE_SUPABASE_URL was never provided to the build — the helper throws a StripeFunctionError with code "unconfigured" before attempting any network call.

Source

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

  portal_url?: string;
  already_subscribed?: boolean;
  error?: string;
}

interface PortalResponse {
  success: boolean;
  url?: string;
  error?: string;
}

async function invoke<T>(
  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> {

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Set VITE_SUPABASE_URL (and VITE_SUPABASE_ANON_KEY) in the SaaS env file and restart the dev server so Vite re-reads env.
  2. Run in SaaS mode: `task frontend:dev` with the saas flavor / MODE=saas, or `task frontend:prepare MODE=saas` to create .env.saas.local.
  3. Confirm the variable is committed in frontend/editor/.env.saas (not only in an uncommitted .local that the build does not load).
  4. If intentional (non-SaaS build), gate the billing UI so these functions are never invoked outside SaaS.

Example fix

# before
VITE_SUPABASE_URL=

# after (frontend/editor/.env.saas)
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=eyJ...
Defensive patterns

Strategy: validation

Validate before calling

function isSaasSupabaseConfigured(): boolean {
  return Boolean(
    getSupabaseClient() &&
    import.meta.env.VITE_SUPABASE_URL,
  );
}

if (!isSaasSupabaseConfigured()) {
  notifications.show({ message: "Billing requires SaaS Supabase config.", color: "red" });
  return;
}

Type guard

function hasSupabaseClient(): boolean {
  return getSupabaseClient() != null;
}

Try / catch

try {
  await createCheckoutSession(req);
} catch (e) {
  if (e instanceof StripeFunctionError && e.code === "unconfigured") {
    notifications.show({ message: "Billing is not configured for this environment.", color: "red" });
  } else throw e;
}

Prevention

When it happens

Trigger: Any Stripe billing call (checkout, quote, invoice, portal) is made in an environment where the Supabase client was never instantiated because VITE_SUPABASE_URL is unset/empty. The client singleton is null, so invoke refuses to proceed.

Common situations: Dev started the frontend in core/proprietary/desktop mode instead of saas; .env.saas was not layered (Vite only loaded .env); the env var was renamed/removed; CI built the portal without the SaaS secrets. Note: ensureSaasSupabase usually throws first if you are not in SaaS mode at all, so reaching this null-check typically means you ARE in SaaS mode but the URL env var is missing.

Related errors


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