mcpField
R
remix-v2-error-boundaries-review

remix-v2-error-boundaries-review

github.com/existential-birdsテスト・品質8541 回インストール11 週間前に更新
リポジトリ

remix-v2-error-boundaries-review について

TypeScript の Remix v2 プロジェクト向けエラーバウンダリのレビュースキルです。ルートのエラー処理や v1 移行時の残存要素を確認し、境界の構造、Response の throw、ルート境界、エラー報告を扱います。

主な機能

  1. 01ErrorBoundary の宣言とエラー分類を確認
  2. 024xx・5xx の throw 方法をレビュー
  3. 03ルート境界のドキュメント構造を確認
  4. 04CatchBoundary など v1 の残存要素を検出
  5. 05免除条件と重大度の指針を提示

活用シーン

  1. 01Remix ルートのエラーバウンダリレビュー
  2. 024xx Response が境界に到達するか確認
  3. 03Remix v1 のエラー処理の残存箇所を調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/existential-birds/beagle --skill remix-v2-error-boundaries-review

インストール後、Claude Code で/remix-v2-error-boundaries-reviewを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
boundary-shape.md
root-boundary.md
throw-response.md
v1-holdovers.md

詳細

Remix v2 のエラーバウンダリーのコードレビュー

@remix-run/* からインポートする TypeScript のルートモジュールが対象です。このトピックに対応する関連ナレッジスキルはありません。標準的な考え方を以下にまとめ、詳しくは references/ で説明します。

v2 のバウンダリーモデル(最初にお読みください)

Remix v2 では、v1 の CatchBoundary と ErrorBoundary が、ルートモジュールの 単一の ErrorBoundary エクスポートに統合されました。フレームワークは、スローされた Response(例:throw new Response(...)、throw json(...))と、スローされたランタイムエラー(loader/action/render の例外)の 両方に対してこれを呼び出します。バウンダリー内では useRouteError() フックで値を取得し、次の順序で型を絞り込みます。

  1. isRouteErrorResponse(error) → Response がスローされています。error.status、error.statusText、error.data を参照します。
  2. error instanceof Error → 実際のランタイムエラーです。error.message を参照します。
  3. それ以外 → 不明な値がスローされています。汎用のフォールバックを表示します。

バウンダリーは props を受け取りません。CatchBoundary、useCatch、future.v2_errorBoundary フラグはすべて廃止されています。これらが見つかった場合は、いずれも v1 の名残です。エラーは最も近い ErrorBoundary で表示され、存在しない場合はルートまでバブルアップします。ルートバウンダリーではドキュメント全体が再マウントされるため、<Meta />、<Links />、<Scripts /> をレンダリングする必要があります。バウンダリーに到達するのは、loader/action の結果が スローされた場合のみです。4xx ステータスで return json(...) を返すのは、成功した loader であり、エラーではありません。サーバー側のランタイムエラーは、任意の entry.server.tsx の handleError エクスポートにも渡されます(スローされた Response は渡されません)。

クイックリファレンス

問題の種類リファレンス
ルートの ErrorBoundary がない、バウンダリーへの props の指定、instanceof Error だけによる型の絞り込み、isRouteErrorResponse だけによる型の絞り込みreferences/boundary-shape.md
4xx/5xx の throw ではなく return、error.data の握りつぶし、文字列のスロー、handleError がないreferences/throw-response.md
ルートバウンダリーがない、<Meta />/<Links />/<Scripts /> のないルートバウンダリー、ルートバウンダリー内での useLoaderData()references/root-boundary.md
CatchBoundary のエクスポート、useCatch のインポート、v2_errorBoundary future フラグreferences/v1-holdovers.md

レビューチェックリスト

  • ErrorBoundary が props なしの export function ErrorBoundary() として宣言されている
  • エラーを props や useCatch() ではなく、useRouteError() で取得している
  • 型の絞り込みでは、最初に isRouteErrorResponse(error) を確認し、次に error instanceof Error を確認してから、フォールバックを使用している
  • error.data を JSX に渡す前に、型を確認・絞り込みし、防御的にレンダリングしている
  • loader/action の 4xx / 5xx では、Response / json に return ではなく throw を使っている
  • スローされる可能性のあるルートが、それぞれ独自の ErrorBoundary をエクスポートしている(ウィジェットのエラーで親を巻き込まない)
  • ルートの app/root.tsx が <Meta />、<Links />、<Scripts /> をレンダリングする ErrorBoundary をエクスポートしている
  • ルートのデータを参照する際、ルートバウンダリーでは useLoaderData() ではなく useRouteLoaderData("root") を使用している
  • CatchBoundary のエクスポート、useCatch のインポートがなく、remix.config.js に future.v2_errorBoundary がない
  • entry.server.tsx が handleError をエクスポートし、ランタイムエラーをエラーレポーターに渡している
  • handleError がスローされた Response も渡されると想定していない(渡されません)
  • スローされる値が Response / json / Error のインスタンスであり、プレーンな文字列や POJO ではない

有効なパターン(指摘しないでください)

以下は正しい Remix v2 の使用方法であり、問題として報告してはいけません。

  • 親から意図的に継承する、ErrorBoundary のないルート — 境界は上位へと連鎖します。子ルートでは ErrorBoundary を省略し、親(またはルート)がフォールバックをレンダリングできます。そのルートでユーザーに固有のエラー UX を処理しており、かつ親の境界では対応できない場合にのみ指摘してください。
  • loader/action から throw new Response(...) または throw json(...) を使用する — 404/401/403 などを通知する標準的な方法です。これは「制御フローに例外を使用する」ことではなく、文書化された v2 の仕様です。
  • isRouteErrorResponse(error) のみで型を絞り込む — そのルートが Response しか throw せず、レンダリング時にクラッシュするリスクもないことが明らかな場合は許容されます。重要度は最大でも ADVISORYです。多層防御として instanceof Error 分岐の追加を提案し、バグとして指摘しないでください。
  • useRouteError() を呼び出さない ErrorBoundary — 意図的に静的な「問題が発生しました」フォールバックをレンダリングする場合は有効です(例:エラーの詳細を表示しないマーケティングページ)。
  • useRouteLoaderData("root") を呼び出して undefined を取得するルートの ErrorBoundary — 文書化された防御的パターンです(ルートの loader が throw した可能性があります)。undefined の処理を「デッドコード」として指摘しないでください。
  • request.signal.aborted の場合に早期リターンする handleError — 文書化されたノイズフィルターであり、エラーの握りつぶしではありません。
  • throw された Response を処理しない handleError — フレームワークの仕様上、handleError が呼び出されるのはランタイムエラーの場合のみです。Response の処理がないのは正しく、欠落ではありません。
  • 裸のフラグメントを返す(<html> / <body> がない)ネストされた ErrorBoundary — ドキュメントを管理するのはルートの境界のみです。ネストされた境界は親レイアウトの内部にレンダリングされるため、ドキュメントタグを含めてはいけません。

重要度のガイダンス

コードベースに別の尺度が文書化されていない限り、以下をデフォルトとして使用してください。

パターンデフォルトの重要度
v2 のコードベースにおける CatchBoundary エクスポート、または useCatch のインポートBLOCKER(ビルドを壊す、またはデッドコード)
ルートの ErrorBoundary に <Scripts /> がないBLOCKER(エラーページで行き詰まる)
({ error }) という v1 のプロパティシグネチャを持つ ErrorBoundaryWARN(実行時に気づかれず undefined になる)
4xx に対して throw の代わりに return json(...) を使用するWARN(boundary が呼び出されない)
レンダリング時にクラッシュするリスクがあるルートで instanceof Error 分岐がないWARN
Response のみを扱うルートで instanceof Error 分岐がないADVISORY
ルートの boundary で useLoaderData()(useRouteLoaderData ではなく)を使用するWARN(潜在的なループ)
entry.server.tsx に handleError がないADVISORY(可観測性の不足であり、バグではない)

指摘を作成する前の必須ゲート

順番に実施してください。報告対象のバッチについて、すべてのゲートを通過するまで、ユーザー向けの指摘を作成してはいけません。

  1. 場所の証拠 — 合格条件: 各問題について、リポジトリ内のルートモジュールのパス(または app/root.tsx、app/entry.server.tsx)と、行の範囲または実際に読んだファイルからの短い引用(記憶や差分だけに基づく推測ではないもの)を記載します。app/root.tsx のパスを示さずに「ルートの境界が間違っています」とする指摘は報告できません。

  2. 適用除外の確認 — 合格条件: 各問題について、有効なパターン(指摘しないもの)の対象ではない理由を、1行で説明できます。特に、ErrorBoundary がないのは親の境界へ意図的にカスケードさせているためではないことを確認してください。また、isRouteErrorResponse のみで絞り込んでいる箇所が、Response しか throw しないことが明確なルートではないことも確認してください(その場合は ADVISORY に格下げします)。

  3. v1 と v2 のマーカー確認 — 合格条件: 指摘を作成する前に、ルートモジュール(および横断的な問題についてはリポジトリ全体)で CatchBoundary、useCatch、v2_errorBoundary、ErrorBoundary({ error、ErrorBoundary({error を grep します。いずれかが見つかった場合、その指摘は v1 の残存コードであり、(references/v1-holdovers.md を読み込んだうえで)そのようにラベル付けする必要があります。「エラーハンドリングの不足」のような一般的な問題として扱ってはいけません。見つからない場合、コードは v2 の形式であり、指摘は v2 の正確性に関するものです。

  4. プロトコル — 合格条件: このレビューについて、レビュー検証プロトコルの「報告前検証チェックリスト」を完了しています。

レビューの確認事項

  1. throw が発生し得るすべてのルート(loader、action、render)に、適切なレベルの ErrorBoundary がありますか?復旧 UI が重要な場合はローカルに、意図的なカスケードの場合は親またはルートに配置されていますか?
  2. 各 ErrorBoundary は useRouteError() を呼び出し(useCatch() や props ではなく)、最初に isRouteErrorResponse で絞り込んでいますか?
  3. 4xx / 5xx の制御フローでは、境界が実際に作動するように return ではなく throw を使用していますか?
  4. app/root.tsx は <Meta />、<Links />、<Scripts /> を含む ErrorBoundary をエクスポートし、useRouteLoaderData("root") を防御的に使用していますか?
  5. v1 のマーカー(CatchBoundary、useCatch、v2_errorBoundary、({ error }) の props シグネチャ)が残っていますか?
  6. 実行時エラーのオブザーバビリティのために entry.server.tsx に handleError があり、正しい契約(Response を扱わない)に従っていますか?

参考ドキュメント

コメント