Stirling-Tools/Stirling-PDF · error · StripeFunctionError

create-checkout-session failed

Error message

create-checkout-session failed

What it means

Fallback thrown by createCheckoutSession when the edge function returns res.success === false but no res.error field. It means the function explicitly reported failure yet omitted an explanation, so the client substitutes a generic message.

Source

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

export async function createCheckoutSession(
  req: CheckoutSessionRequest,
): Promise<CheckoutSession> {
  const res = await invoke<CheckoutResponse>("create-checkout-session", {
    team_id: req.teamId,
    currency: req.currency ?? "usd",
    success_url: req.successUrl,
    cancel_url: req.cancelUrl,
    // The portal drives an in-page onComplete handler (the checkout modal stays open to
    // finalise activation + nudge the linked instance), so tell the edge function not to
    // redirect on completion. A redirect would reload the page, skip that finalize step, and
    // make Stripe ignore onComplete entirely (console warns "redirect_on_completion: always").
    redirect_on_completion: "never",
    ...(req.billingOwnerEmail
      ? { billing_owner_email: req.billingOwnerEmail }
      : {}),
  });
  if (!res.success) {
    throw new StripeFunctionError(
      res.error ?? "create-checkout-session failed",
    );
  }
  const alreadySubscribed = Boolean(res.already_subscribed);
  const redirectUrl = alreadySubscribed
    ? (res.portal_url ?? null)
    : (res.url ?? null);
  const clientSecret = alreadySubscribed ? null : (res.client_secret ?? null);
  if (!clientSecret && !redirectUrl) {
    throw new StripeFunctionError(
      "create-checkout-session returned neither client_secret nor URL",
    );
  }
  return {
    clientSecret,
    redirectUrl,
    alreadySubscribed,
  };

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Open the create-checkout-session edge function logs in the Supabase dashboard to find the real failure branch.
  2. Ensure every failure branch in the function returns { success: false, error: '<detail>' }.
  3. Verify Stripe secret key + price/price-tier config the function reads.
  4. Catch StripeFunctionError at the call site and surface a user-facing message; retry after fixing config.

Example fix

// edge function — before
return new Response(JSON.stringify({ success: false }), { status: 200 });

// after
return new Response(
  JSON.stringify({ success: false, error: 'Stripe price not found for currency' }),
  { status: 200, headers: { 'Content-Type': 'application/json' } },
);
Defensive patterns

Strategy: try-catch

Type guard

function isCheckoutFailure(res: CheckoutResponse): res is CheckoutResponse & { success: false } {
  return res.success === false;
}

Try / catch

try {
  const { clientSecret, redirectUrl } = await createCheckoutSession(req);
  startEmbeddedCheckout(clientSecret, redirectUrl);
} catch (e) {
  if (e instanceof StripeFunctionError) {
    notifications.show({ message: e.message, color: "red" });
  } else throw e;
}

Prevention

When it happens

Trigger: create-checkout-session responds { success: false } (no error string). The right-hand operand of `res.error ?? fallback`, so it only fires when error is null/undefined/empty.

Common situations: Edge function hit an error branch that returns { success: false } without setting error; a Stripe API failure inside the function was swallowed; a refactored response shape dropped the error field.

Related errors


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