From 7e8011976cfec5bfdf5f5210de89d0b9df7da016 Mon Sep 17 00:00:00 2001 From: harsh mahajan Date: Mon, 27 Jul 2026 19:24:28 +0530 Subject: [PATCH] fix: prevent project Usage tab crash on missing usage breakdowns The project Settings > Usage tab failed to render entirely, while every other Settings tab worked. The API request succeeded; the crash was in the page component's render: Uncaught TypeError: Cannot read properties of undefined (reading 'length') at +page.svelte:324 `data.usage.executionsBreakdown` was undefined. The console SDK types it as a required `MetricBreakdown[]`, so the guard read `.length` on it unconditionally. Because a throw during render aborts the whole render pass, nothing mounted -- which is why the tab highlight moved to Usage while the previously active tab's content stayed on screen instead of an error page being shown. Optional-chain both breakdown-table guards (`executionsBreakdown` and `authPhoneCountryBreakdown`) so a missing breakdown hides just its table. Also guard two latent instances of the same class on this page, since the response demonstrably omits array fields the SDK types as required: - `legendData` reduced over `databasesReads`/`databasesWrites` outside any `{#if}`, so it would have crashed even earlier had those been missing. - The db chart's `{#if dbReads || dbWrites}` passes when only one array is present, but the body mapped both. These are null-guards rather than defaulting the arrays to `[]`, because `[]` is truthy and would flip several `{#if}` empty-state checks, silently replacing "No data to show" cards with empty charts. Separately, the attribution ping in `+layout.svelte` was fire-and-forget with no `.catch()`, so a blocked request surfaced as an unhandled `Failed to fetch` rejection that obscured real errors in the console. --- .../settings/usage/[[invoice]]/+page.svelte | 12 ++++++------ src/routes/+layout.svelte | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte b/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte index 085865d6de..50e94d3ff0 100644 --- a/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/settings/usage/[[invoice]]/+page.svelte @@ -49,11 +49,11 @@ $: legendData = [ { name: 'Reads', - value: clampMin(data.usage.databasesReads.reduce((sum, item) => sum + item.value, 0)) + value: clampMin((dbReads ?? []).reduce((sum, item) => sum + item.value, 0)) }, { name: 'Writes', - value: clampMin(data.usage.databasesWrites.reduce((sum, item) => sum + item.value, 0)) + value: clampMin((dbWrites ?? []).reduce((sum, item) => sum + item.value, 0)) } ]; @@ -196,11 +196,11 @@ series={[ { name: 'Reads', - data: [...dbReads.map((e) => [e.date, e.value])] + data: [...(dbReads ?? []).map((e) => [e.date, e.value])] }, { name: 'Writes', - data: [...dbWrites.map((e) => [e.date, e.value])] + data: [...(dbWrites ?? []).map((e) => [e.date, e.value])] } ]} /> @@ -321,7 +321,7 @@ data: [...executions.map((e) => [e.date, e.value])] } ]} /> - {#if data.usage.executionsBreakdown.length > 0} + {#if data.usage.executionsBreakdown?.length > 0} Function @@ -579,7 +579,7 @@

- {#if data.usage.authPhoneCountryBreakdown.length > 0} + {#if data.usage.authPhoneCountryBreakdown?.length > 0} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 07ff6a6612..9a024b821d 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -58,7 +58,11 @@ } if (ref || referrer || utmSource || utmCampaign || utmMedium) { - sdk.forConsole.sources.create(ref, referrer, utmSource, utmCampaign, utmMedium); + // fire-and-forget: swallow failures so a blocked request doesn't + // surface as an unhandled rejection + sdk.forConsole.sources + .create(ref, referrer, utmSource, utmCampaign, utmMedium) + .catch(() => {}); } if (referrer || ref) {