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) {