SvelteKitのエラーハンドリング(+error.svelteとerror())を整理した話

SvelteKitでエラーが起きたときの処理方法を整理します。+error.svelte でエラー画面を作り、error() ヘルパーでエラーを投げる方法と、コンポーネント内でのエラー処理の違いを解説します。


エラーの種類

SvelteKitのエラーは2種類あります。

予期されたエラー(Expected Error)
  → error() ヘルパーで意図的に投げるエラー
  → 例:404 Not Found、403 Forbidden

予期しないエラー(Unexpected Error)
  → バグや予期しない例外
  → 例:DBへの接続失敗、nullアクセス

① +error.svelte でエラー画面を作る

+error.svelte を置くとエラー時に表示されます。

<!-- src/routes/+error.svelte -->
<script lang="ts">
  import { page } from '$app/stores';
</script>

<h1>{$page.status} エラー</h1>
<p>{$page.error?.message}</p>
<a href="/">トップに戻る</a>

$page.status でHTTPステータスコード(404・500など)、$page.error.message でエラーメッセージを取得できます。


② load関数でエラーを投げる

// src/routes/posts/[id]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ params, platform }) => {
  const post = await platform!.env.DB
    .prepare('SELECT * FROM posts WHERE id = ?')
    .bind(params.id)
    .first();

  if (!post) {
    error(404, '記事が見つかりません');  // +error.svelte が表示される
  }

  return { post };
};

error(ステータスコード, メッセージ) を呼ぶと処理が止まって +error.svelte が表示されます。


③ ネストした +error.svelte

+error.svelte はルートごとに置けます。近いほうが優先されます。

src/routes/
  +error.svelte           ← 全体のフォールバック
  posts/
    +error.svelte         ← /posts 配下のエラーはこちらが表示される
    [id]/
      +page.server.ts
<!-- src/routes/posts/+error.svelte -->
<script lang="ts">
  import { page } from '$app/stores';
</script>

<h2>記事が見つかりませんでした({$page.status})</h2>
<p>{$page.error?.message}</p>
<a href="/">トップに戻る</a>

④ コンポーネント内でのエラー処理

+error.svelte が表示されるのは load 関数や actions の中で error() を呼んだときだけです。ボタン押下などのイベントハンドラは「ページが既に表示された後」に動くため、SvelteKitがエラーをキャッチできません。

+error.svelte が表示されるケース:
  ✅ load関数の中で error(404, '...') を呼んだとき
  ✅ actions の中で error() を呼んだとき

+error.svelte が表示されないケース:
  ❌ ボタン押下などのイベントハンドラ内
  ❌ コンポーネント内の fetch 処理
  ❌ タイマーの中

イベントハンドラなどでは try/catch で自前でエラーを捕まえて、$state に保持して画面に表示します。

<script lang="ts">
  let errorMessage = $state('');

  async function handleSubmit() {
    try {
      const res = await fetch('/api/submit', { method: 'POST' });
      if (!res.ok) {
        errorMessage = `送信に失敗しました(${res.status})`;
        return;
      }
      // 成功処理
    } catch (e) {
      errorMessage = 'ネットワークエラーが発生しました';
    }
  }
</script>

{#if errorMessage}
  <p style="color: red">{errorMessage}</p>
{/if}

<button onclick={handleSubmit}>送信</button>

まとめ

状況 対応方法
load関数で404・403を返す error(404, 'メッセージ')
エラー画面をカスタマイズ +error.svelte を作成
ルートごとに異なるエラー画面 各ディレクトリに +error.svelte を置く
コンポーネント内の非同期処理 try/catch$state にエラーを保持
← トップページに戻る