{"record":{"id":"f133e22cfcd067a9","repo":"Stirling-Tools/Stirling-PDF","slug":"failed-to-fetch-plans-error-message","errorCode":null,"errorMessage":"Failed to fetch plans: ${error.message}","messagePattern":"Failed to fetch plans: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/services/licenseService.ts","lineNumber":147,"sourceCode":"      const { data, error } = await supabase.functions.invoke<{\n        prices: Record<\n          string,\n          {\n            unit_amount: number;\n            currency: string;\n            lookup_key: string;\n          }\n        >;\n        missing: string[];\n      }>(\"stripe-price-lookup\", {\n        body: {\n          lookup_keys: SELF_HOSTED_LOOKUP_KEYS,\n          currency,\n        },\n      });\n\n      if (error) {\n        throw new Error(`Failed to fetch plans: ${error.message}`);\n      }\n\n      if (!data || !data.prices) {\n        throw new Error(\"No pricing data returned\");\n      }\n\n      // Log missing prices for debugging\n      if (data.missing && data.missing.length > 0) {\n        console.warn(\n          \"Missing Stripe prices for lookup keys:\",\n          data.missing,\n          \"in currency:\",\n          currency,\n        );\n      }\n\n      // Build price map for easy access\n      const priceMap = new Map<","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/services/licenseService.ts#L129-L165","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect error.message — for a FunctionsRelayError it names the function; for a Stripe error it carries the Stripe message.","Confirm the stripe-price-lookup edge function is deployed and has STRIPE_SECRET_KEY set in its secrets.","Verify all SELF_HOSTED_LOOKUP_KEYS exist as lookup keys on the Stripe products.","Catch and fall back to static plans (per error 166's contract) if dynamic pricing is non-critical."],"exampleFix":"// before\nconst { data, error } = await supabase.functions.invoke('stripe-price-lookup', { body });\nif (error) throw new Error(`Failed to fetch plans: ${error.message}`);\n\n// after — keep the throw, but surface a retry + static fallback at the caller\ntry { return await licenseService.getPlans(f, h, c); }\ncatch (e) {\n  if (/Failed to fetch plans/.test(e.message)) { return STATIC_PLANS; }\n  throw e;\n}","handlingStrategy":"fallback","validationCode":"// nothing to validate client-side beyond isSupabaseConfigured; the error originates in the edge function\nif (!isSupabaseConfigured) return STATIC_PLANS;","typeGuard":null,"tryCatchPattern":"try {\n  return await licenseService.getPlans(features, highlights, currency);\n} catch (e) {\n  if (/Failed to fetch plans/.test(e instanceof Error ? e.message : \"\")) {\n    logToMonitoring(e);\n    return { plans: STATIC_PLANS };\n  }\n  throw e;\n}","preventionTips":["Confirm stripe-price-lookup is deployed with STRIPE_SECRET_KEY before relying on dynamic pricing.","Keep SELF_HOSTED_LOOKUP_KEYS in sync with the actual Stripe lookup keys.","Treat dynamic pricing as progressive enhancement; always have static plans ready."],"tags":["supabase","edge-function","stripe","license","plans","proprietary","typescript"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}