Stirling-Tools/Stirling-PDF · error · StripeFunctionError
quote PDF fetch failed
Error message
quote PDF fetch failed
What it means
Fallback message in fetchBundleQuotePdf when the GET invocation of create-payg-bundle-quote returns an error whose .message is falsy. The PDF fetch failed at the edge function, but the error carried no human-readable text, so the helper substitutes a generic description.
Source
Thrown at frontend/editor/src/portal/billing/stripe.ts:478
/**
* 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",
);
}
const { data, error } = await supabase.functions.invoke<Blob>(
`create-payg-bundle-quote?quote_id=${quoteId}`,
{ method: "GET" },
);
if (error) {
throw new StripeFunctionError(error.message ?? "quote PDF fetch failed");
}
if (!(data instanceof Blob)) {
throw new StripeFunctionError("quote PDF response was not a file");
}
return data;
}
/**
* {@code VITE_STRIPE_PUBLISHABLE_KEY} — the Stripe pk used by embedded Checkout. Coalesces to "" when
* unset so the declared `string` return type is honest (Vite substitutes `undefined` for a missing
* env var); callers guard with a falsy check.
*/
export function getStripePublishableKey(): string {
return import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY ?? "";
}
// Process-wide memoized Stripe.js loader, shared by the embedded-checkout modals so the SDK promise
// is created once rather than per-modal. loadStripe is dynamically imported so its chunk only loadsView on GitHub (pinned to 9ef20dcab8)
Solutions
- Inspect the full StripeFunctionError / underlying error in dev tools for a code or cause, even when message is empty.
- Check the create-payg-bundle-quote GET route logs in the Supabase dashboard.
- Verify quoteId is valid and the GET route accepts ?quote_id=.
- Confirm CORS and content-type handling on the GET route (it streams application/pdf).
Example fix
// before
throw new StripeFunctionError(error.message ?? "quote PDF fetch failed");
// after — expose more of the underlying error
const e = error as { code?: string; hint?: string };
throw new StripeFunctionError(
error.message ?? `quote PDF fetch failed (code=${e.code ?? "?"})`,
e.code,
); Defensive patterns
Strategy: try-catch
Type guard
function isFunctionsError(e: unknown): e is { message?: string; code?: string } {
return typeof e === "object" && e !== null && "message" in e;
} Try / catch
try {
const pdf = await fetchBundleQuotePdf(quoteId);
downloadBlob(pdf);
} catch (e) {
if (e instanceof StripeFunctionError) {
notifications.show({ message: `Could not download quote PDF: ${e.message}`, color: "red" });
}
} Prevention
- Inspect the underlying FunctionsError in dev tools for code/cause when message is empty.
- Confirm CORS + application/pdf streaming on the GET route.
- Pin @supabase/supabase-js to avoid error-shape drift.
When it happens
Trigger: supabase.functions.invoke against the create-payg-bundle-quote GET route rejects with an error object lacking message (or with message === ''). This is the right-hand operand of `error.message ?? fallback`.
Common situations: Transport/CORS error on the GET route; edge function threw a non-Error value; the function returned an error status with an empty body; supabase-js version changed the error shape.
Related errors
- Edge function ${name} failed
- quote PDF response was not a file
- Edge function ${name} returned no data
- RPC ${fn} failed
- create-checkout-session failed
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/9d08b03f3f6a5f35.
Report an issue: GitHub.