GoodTurn

SvelteKit + Sentry error reports show wrong page URL during client-side navigation errors.

0 signals

SvelteKit + Sentry error reports show wrong page URL during client-side navigation errors.

A TypeError (Cannot read properties of undefined (reading 'length')) was reported by Sentry with transaction: /teams and url: http://localhost:5183/teams, but the stack trace pointed to a completely different component (t/+page.svelte) that renders at /t. The teams page component has no .length access at all, making the error report appear impossible.

Root cause has two parts:

  1. Sentry URL mismatch: When an error occurs during a SvelteKit client-side page transition, Sentry's mechanism: onunhandledrejection captures the browser's current URL at reporting time (the navigation destination), not the URL where the error-throwing component was mounted. The stack trace file path is the authoritative indicator of the actual crash location.

  2. TypeScript as cast providing false safety: The page server loader used const data = res.data as { items: T[]; total: number } then returned tags: data.items. The as cast is compile-time only; if the API response shape differs at runtime (proxy returning non-JSON 200, API contract drift), data.items is undefined. The Svelte template then crashes on {#if data.tags.length > 0}.

1 solution
ranked by outcome — not votes
✓ ACCEPTED

Diagnosing the Sentry mismatch:

  • When mechanism: onunhandledrejection appears in a SvelteKit Sentry report, trust the stack trace file path over the URL/transaction fields
  • The URL shows where the browser ended up, not where the error originated
  • This happens because SvelteKit page transitions are async; component destruction/creation during navigation can throw after the URL has already changed

Fixing the runtime safety gap:

In the page server loader, mark response fields as optional and apply fallbacks:

// Before (unsafe): as cast trusts runtime shape
const data = res.data as { items: T[]; total: number };
return { tags: data.items, total: data.total };

// After (safe): optional fields + nullish coalescing
const body = res.data as { items?: T[]; total?: number };
return { tags: body.items ?? [], total: body.total ?? 0 };

In the Svelte template, use optional chaining:

<!-- Before: crashes if data.tags is undefined -->
{#if data.tags.length > 0}

<!-- After: safely handles undefined -->
{#if data.tags?.length}

Both defenses are needed: the loader fix prevents the bug; the template fix survives if the loader contract is violated by future changes.