# SvelteKit error handling


The `@flareapp/sveltekit` package provides error hooks that integrate with SvelteKit's `handleError` hook in both `hooks.client.ts` and `hooks.server.ts`. These hooks catch unhandled errors and report them to Flare with SvelteKit-specific context including the current route, URL, params, and query parameters.

> **Note:** Server-side error handling is currently **experimental**. Flare does not yet have access to server-side source files, so server error reports will have limited stack trace resolution.

## Client-side error handling

Set up the error hook in `src/hooks.client.ts`:

```ts
// src/hooks.client.ts
import { flare } from '@flareapp/js';
import { handleErrorWithFlare } from '@flareapp/sveltekit/client';

flare.light('YOUR PROJECT KEY');

export const handleError = handleErrorWithFlare();
```

The client-side hook automatically:

1. Skips 4xx errors (expected client-side responses),
2. Unwraps SvelteKit error objects,
3. Builds route context from `$app/state` (current URL, route ID, params, redacted query params),
4. Reports the error to Flare,
5. Starts [route context tracking](/docs/svelte/data-collection/adding-custom-context#route-context) so every subsequent error includes the current route.

## Server-side error handling (experimental)

Set up the error hook in `src/hooks.server.ts`:

```ts
// src/hooks.server.ts
import { flare } from '@flareapp/js';
import { handleErrorWithFlare } from '@flareapp/sveltekit/server';

flare.light('YOUR PROJECT KEY');

export const handleError = handleErrorWithFlare();
```

The server-side hook works the same way as the client-side hook, except it extracts route context from the SvelteKit `RequestEvent` object instead of `$app/state`.

## SvelteKit context

Each error report includes SvelteKit-specific context under the `svelte.svelteKit` key:

```json
{
  "context": {
    "svelte": {
      "componentName": null,
      "componentHierarchy": [],
      "errorOrigin": "unknown",
      "svelteKit": {
        "routeId": "/users/[id]",
        "url": "/users/42",
        "params": { "id": "42" },
        "query": { "tab": "settings" },
        "status": 500,
        "message": "Internal Error"
      }
    }
  }
}
```

- **`routeId`** - The SvelteKit route pattern (e.g. `/users/[id]`), or `null` if unknown.
- **`url`** - The pathname without the origin.
- **`params`** - Route parameters extracted from the URL.
- **`query`** - Query parameters, with sensitive keys redacted (password, token, secret, authorization, cookie, api_key, session, csrf, etc.).
- **`status`** - The HTTP status code (e.g. `500`).
- **`message`** - The error message from SvelteKit's error handler.

## Lifecycle hooks

`handleErrorWithFlare()` accepts an options object with three lifecycle hooks. All callbacks receive `{ error, status, message }` and, for `beforeSubmit`/`afterSubmit`, a `context` object:

```ts
// src/hooks.client.ts
import { flare } from '@flareapp/js';
import { handleErrorWithFlare } from '@flareapp/sveltekit/client';

flare.light('YOUR PROJECT KEY');

export const handleError = handleErrorWithFlare({
    beforeEvaluate: ({ error, status, message }) => {
        flare.addContext('user', { id: currentUser.id });
    },
    beforeSubmit: ({ error, status, message, context }) => {
        return {
            ...context,
            svelte: {
                ...context.svelte,
                customField: 'value',
            },
        };
    },
    afterSubmit: ({ error, status, message, context }) => {
        console.error('Error reported to Flare:', error.message);
    },
});
```

The three hooks fire in this order:

1. **`beforeEvaluate`** - called after the error is caught, before building the route context. Use this to attach custom context to Flare (e.g. user info, feature flags).
2. **`beforeSubmit`** - called with the built context, must return a (possibly modified) context object. Use this to filter or enrich the report context before it is sent.
3. **`flare.report()`** - the error is reported to Flare.
4. **`afterSubmit`** - called after the report is initiated. The report is sent asynchronously, so this callback runs before the network request completes.

These hooks are separate from `flare.configure({ beforeEvaluate, beforeSubmit })`. The handler hooks add context and run side effects but cannot suppress errors. To filter errors, use the client-level hooks:

```js
flare.configure({
    beforeEvaluate: (error) => {
        if (error.message.includes('Boring error')) {
            return false; // Don't report this error
        }

        return error;
    },
});
```

See the [client hooks documentation](/docs/javascript/errors/client-hooks) for more details.

## Chaining with existing error handlers

You can pass an existing `handleError` function directly to `handleErrorWithFlare()`, and it will be called after the error is reported to Flare:

```ts
// src/hooks.client.ts
import { flare } from '@flareapp/js';
import { handleErrorWithFlare } from '@flareapp/sveltekit/client';

flare.light('YOUR PROJECT KEY');

const myErrorHandler = ({ error, status, message }) => {
    console.error('Custom handler:', error);
};

export const handleError = handleErrorWithFlare(myErrorHandler);
```

## 4xx error skipping

`handleErrorWithFlare()` automatically skips errors with 4xx status codes (400 to 499), since these represent expected client-side responses like 404 Not Found or 403 Forbidden, not application errors.

If you need to capture specific 4xx errors, use [`captureError()`](#capturing-errors-manually) instead, which does not skip any status codes.

## Capturing errors manually

The `captureError()` function lets you manually report errors to Flare with SvelteKit route context. Unlike `handleErrorWithFlare()`, it does not skip 4xx errors. You control which errors to report.

This is useful for errors you catch yourself in `load` functions, form actions, API routes, or event handlers that you want to report to Flare without re-throwing.

### Client-side usage

On the client, `captureError()` automatically picks up route context from `$app/state`:

```ts
import { captureError } from '@flareapp/sveltekit/client';

try {
    await riskyOperation();
} catch (error) {
    captureError(error);
}
```

You can optionally pass `status` and `message` for additional context:

```ts
captureError(error, {
    status: 503,
    message: 'Service temporarily unavailable',
});
```

### Server-side usage (experimental)

On the server, pass the `RequestEvent` as the `event` option so `captureError()` can extract route context:

```ts
// src/routes/api/checkout/+server.ts
import { captureError } from '@flareapp/sveltekit/server';

export async function POST(event) {
    try {
        await processPayment(event);
    } catch (error) {
        captureError(error, { event });

        return new Response('Payment failed', { status: 500 });
    }
}
```

In `load` functions and form actions:

```ts
// src/routes/dashboard/+page.server.ts
import { captureError } from '@flareapp/sveltekit/server';

export async function load(event) {
    try {
        return { data: await fetchDashboardData() };
    } catch (error) {
        captureError(error, {
            event,
            status: 500,
            message: 'Failed to load dashboard data',
        });

        return { data: null };
    }
}
```

### Differences from `handleErrorWithFlare()`

| | `handleErrorWithFlare()` | `captureError()` |
|---|---|---|
| Usage | SvelteKit `handleError` hook | Manual, anywhere in your code |
| Skips 4xx errors | Yes | No |
| Route context (client) | From `$app/state` | From `$app/state` |
| Route context (server) | From SvelteKit's hook parameter | From `options.event` |
| Lifecycle hooks | `beforeEvaluate`, `beforeSubmit`, `afterSubmit` | None (use `flare.configure()` for filtering) |

## Options reference

### `handleErrorWithFlare()` options

| Option | Type | Description |
|--------|------|-------------|
| `beforeEvaluate` | `({ error, status, message }) => void` | Called before route context is built. |
| `beforeSubmit` | `({ error, status, message, context }) => FlareSvelteKitContext` | Called before submitting; must return a (possibly modified) context. |
| `afterSubmit` | `({ error, status, message, context }) => void` | Called after `flare.report()` is called (the report is sent asynchronously). |

### `captureError()` options

| Option | Type | Description |
|--------|------|-------------|
| `event` | `RequestEvent` (server only) | The SvelteKit request event, used to extract route context. Ignored on the client. |
| `status` | `number` | Optional HTTP status code to attach to the report. |
| `message` | `string` | Optional message to attach to the report. |
