Stirling-Tools/Stirling-PDF · error · StripeFunctionError
cancel-payg-bundle-quote failed
Error message
cancel-payg-bundle-quote failed
What it means
Fallback thrown by cancelBundleQuote when cancel-payg-bundle-quote returns success === false. Cancellation reverts a quote to an open/editable row and charges nothing (capacity is only credited on invoice.paid), so a failed cancel is surfaced but is often a recoverable race rather than data loss. The comment explicitly names invoice_already_paid as a typical cause.
Source
Thrown at frontend/editor/src/portal/billing/stripe.ts:454
};
}
/**
* Cancel an unpaid prepaid-bundle purchase via {@code cancel-payg-bundle-quote}: the edge fn voids the
* invoice (delete if draft, void if finalized), best-effort cancels the Stripe quote, and voids the quote
* row so the buyer can start over. Nothing was charged (capacity is credited on invoice.paid), so there's
* no refund. Throws a StripeFunctionError on failure (e.g. {@code invoice_already_paid}).
*/
export async function cancelBundleQuote(req: {
teamId: number;
quoteId: number;
}): Promise<void> {
const res = await invoke<{ success?: boolean; error?: string }>(
"cancel-payg-bundle-quote",
{ team_id: req.teamId, quote_id: req.quoteId },
);
if (!res.success) {
throw new StripeFunctionError(
res.error ?? "cancel-payg-bundle-quote failed",
);
}
}
/**
* Fetch the Stripe-rendered quote PDF for a persisted quote, via the {@code create-payg-bundle-quote}
* GET route (streams application/pdf). Returns a Blob the caller can object-URL for download.
*/
export async function fetchBundleQuotePdf(quoteId: number): Promise<Blob> {
ensureSaasSupabase();
const supabase = getSupabaseClient();
if (!supabase) {
throw new StripeFunctionError(
"SaaS Supabase not configured — set VITE_SUPABASE_URL.",
"unconfigured",
);
}View on GitHub (pinned to 9ef20dcab8)
Solutions
- Treat cancel failure as expected: catch StripeFunctionError and inspect the code (e.g. invoice_already_paid) before toasting.
- Refresh the quote state after a failed cancel — it may already be paid/cancelled.
- Disable the Cancel control once the quote is in a non-cancellable status.
- Ensure the function returns a stable error code so the UI can branch gracefully.
Example fix
// before
await cancelBundleQuote({ teamId, quoteId });
// after
try {
await cancelBundleQuote({ teamId, quoteId });
} catch (e) {
if (e instanceof StripeFunctionError && /already.*paid/i.test(e.message)) {
notifications.show({ message: "Invoice already paid — cannot cancel.", color: "orange" });
refreshQuote();
return;
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
function canCancelQuote(quote: LatestBundleQuote | null): boolean {
return quote != null && quote.status !== "cancelled" && quote.status !== "paid";
}
if (!canCancelQuote(latestQuote)) {
notifications.show({ message: "Quote cannot be cancelled.", color: "orange" });
return;
} Type guard
function isCancelOk(res: { success?: boolean; error?: string }): boolean {
return res.success === true;
} Try / catch
try {
await cancelBundleQuote({ teamId, quoteId });
} catch (e) {
if (e instanceof StripeFunctionError && /already.*paid/i.test(e.message)) {
notifications.show({ message: "Invoice already paid — cannot cancel.", color: "orange" });
refreshQuote();
return;
}
throw e;
} Prevention
- Treat cancel failure as an expected race, not a fatal error — catch and inspect the code.
- Refresh quote state after a failed cancel; it may already be paid/cancelled.
- Disable the Cancel control once the quote is non-cancellable.
When it happens
Trigger: cancel-payg-bundle-quote responds success:false — most often because the quote's invoice was already paid (you cannot cancel a paid/finalized invoice), or the quote was already cancelled, or the function hit a Stripe error.
Common situations: User clicks Cancel after the invoice was paid in another tab; cancel on a quote whose accept+finalize already completed; concurrent cancel/accept; Stripe-side rejection inside the function.
Related errors
- accept-payg-bundle-quote failed
- create-payg-bundle-quote failed
- finalize-payg-bundle-invoice failed
- Edge function ${name} failed
- Edge function ${name} returned no data
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/daa1f94aac8cf4ab.
Report an issue: GitHub.