Stirling-Tools/Stirling-PDF · error · Error

Failed to fetch plans: ${error.message}

Error message

Failed to fetch plans: ${error.message}

What it means

Thrown by getPlans after supabase.functions.invoke('stripe-price-lookup', ...) returns a non-null error object. Supabase edge-function invocation resolves with {data, error} rather than throwing, so the code inspects error and throws a descriptive Error wrapping error.message. The underlying cause is the edge function failing: missing Stripe keys, a bad lookup key, an exception inside the function, or a FunctionsError payload.

Source

Thrown at frontend/editor/src/proprietary/services/licenseService.ts:147

      const { data, error } = await supabase.functions.invoke<{
        prices: Record<
          string,
          {
            unit_amount: number;
            currency: string;
            lookup_key: string;
          }
        >;
        missing: string[];
      }>("stripe-price-lookup", {
        body: {
          lookup_keys: SELF_HOSTED_LOOKUP_KEYS,
          currency,
        },
      });

      if (error) {
        throw new Error(`Failed to fetch plans: ${error.message}`);
      }

      if (!data || !data.prices) {
        throw new Error("No pricing data returned");
      }

      // Log missing prices for debugging
      if (data.missing && data.missing.length > 0) {
        console.warn(
          "Missing Stripe prices for lookup keys:",
          data.missing,
          "in currency:",
          currency,
        );
      }

      // Build price map for easy access
      const priceMap = new Map<

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Inspect error.message — for a FunctionsRelayError it names the function; for a Stripe error it carries the Stripe message.
  2. Confirm the stripe-price-lookup edge function is deployed and has STRIPE_SECRET_KEY set in its secrets.
  3. Verify all SELF_HOSTED_LOOKUP_KEYS exist as lookup keys on the Stripe products.
  4. Catch and fall back to static plans (per error 166's contract) if dynamic pricing is non-critical.

Example fix

// before
const { data, error } = await supabase.functions.invoke('stripe-price-lookup', { body });
if (error) throw new Error(`Failed to fetch plans: ${error.message}`);

// after — keep the throw, but surface a retry + static fallback at the caller
try { return await licenseService.getPlans(f, h, c); }
catch (e) {
  if (/Failed to fetch plans/.test(e.message)) { return STATIC_PLANS; }
  throw e;
}
Defensive patterns

Strategy: fallback

Validate before calling

// nothing to validate client-side beyond isSupabaseConfigured; the error originates in the edge function
if (!isSupabaseConfigured) return STATIC_PLANS;

Try / catch

try {
  return await licenseService.getPlans(features, highlights, currency);
} catch (e) {
  if (/Failed to fetch plans/.test(e instanceof Error ? e.message : "")) {
    logToMonitoring(e);
    return { plans: STATIC_PLANS };
  }
  throw e;
}

Prevention

When it happens

Trigger: The stripe-price-lookup edge function throws (Stripe secret key missing/misconfigured on the Supabase project); one of SELF_HOSTED_LOOKUP_KEYS isn't registered in Stripe; the function times out or hits a cold-start error; Supabase returns a FunctionsHttpError/FetchError.

Common situations: Stripe price lookup keys (selfhosted:server:monthly etc.) not created in the connected Stripe account; edge function deployed without STRIPE_SECRET_KEY env var; currency param unsupported by the function; Supabase project paused/regional outage.

Related errors


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