mcpField
E
error-handling

error-handling

github.com/epicenterhq開発4.8k82 回インストール3 週間前に更新
リポジトリ

error-handling について

Epicenter プロジェクトで wellcrafted を使い、例外と `Result` 値を扱うためのコーディングスキルです。エラー契約を明確にし、型付きの失敗を保ち、すべてのエラー分岐を処理する方法を示します。

主な機能

  1. 01例外と `Result` の契約選択
  2. 02同期・非同期処理のラップ
  3. 03既知の失敗を変換し未知の例外を再送出
  4. 04すべての `Err` 分岐の処理
  5. 05非同期結果の所有者を明確化

活用シーン

  1. 01例外を送出する API を `Result` 戻り値に変換
  2. 02未知の例外をプログラムエラーとして維持
  3. 03`Result` の消費経路の確認

インストール

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

$npx skills add https://github.com/epicenterhq/epicenter --skill error-handling

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

同梱ファイル

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

references/
http-boundaries.md
toast-on-error.md
wrapping-boundaries.md

詳細

エラー処理

このスキルでは、スローされる例外と Result 値の境界、および Result を適切に処理する方法を扱います。バリアントの設計には define-errors、診断には logging、RPC の提示には query-layer、レスポンス API には hono を組み合わせてください。

信頼できる情報源

Wellcrafted の動作に関する主張はすべて、公式の wellcrafted-dev/wellcrafted のソースコードとテストに基づいてください。このガイダンスを保守する際は、Epicenter にインストールされているバージョンが参照しているソースのバージョンと一致することを確認してください。一致しない場合は、バージョンのずれを報告してください。依存関係の最新化は、このスキルの範囲外です。他のスキル、例、生成されたドキュメント、DeepWiki は手がかりとして扱い、信頼できる情報源とは見なさないでください。

必要に応じて、対象を絞ったリファレンスを参照してください。

  • 1 つの trySync または tryAsync でどの範囲の処理を包むか、特にクリーンアップに関して判断する場合は、references/wrapping-boundaries.md を参照してください。
  • UI コードでタグ付きの失敗を提示する場合は、references/toast-on-error.md を参照してください。
  • 失敗を Hono のレスポンスにマッピングする場合や、伝播させるべき例外を判断する場合は、references/http-boundaries.md を参照してください。

まず契約を選ぶ

必要な契約パターン
呼び出し元が Result<T, E> を受け取るスローする処理を trySync または tryAsync で適合させる
失敗時に有効なフォールバックがあるcatch から Ok(fallback) を返す
失敗をデータとして伝播させる必要があるcatch から型付きの defineErrors ファクトリーの結果を返す
既知の外部エラーだけを Err にする既知の例外をマッピングし、未知の例外は再スローする
周囲の API が例外ベースであるそのスローする契約を維持する
成功、失敗、キャンセル、早期リターンのいずれでもクリーンアップを実行する必要があるfinally を使う

同期処理には trySync を、Promise を返す処理には tryAsync を使ってください。

const { data: response, error } = await tryAsync({
	try: () => fetch(url),
	catch: (cause) => RequestError.TransportFailed({ cause }),
});
if (error !== null) return Err(error);
return Ok(response);

defineErrors ファクトリーは、すでに Err(...) を返します。生の cause をファクトリーに渡し、extractErrorMessage を使ったメッセージの組み立てはファクトリーに任せてください。catch 境界で生の Err(cause) を使わないでください。スローされる値は null または undefined の場合があり、型付けされていない cause ではドメインの失敗を表せません。

意図的なスロー境界を越える

呼び出し元が回復、報告、再試行、または伝播できる間は、Result をデータとして保持してください。周囲の API が失敗を例外で伝える場合に限り、unwrap を使ってください。

import { unwrap } from 'wellcrafted/result';

const document = unwrap(await openDocument(id));

unwrap は Ok.data を返し、Err.error をスローします。このスローは境界の契約であり、失敗が予期せぬものだったことを示すものではありません。この関数で回復、コンテキストの追加、またはクリーンアップを行う必要がある場合は、代わりに error !== null を条件にしてください。

起こりうるすべての Err 分岐を処理する

  • 値が Result<T, E> になりうる場合は、エラー分岐を確認するか、意図的に転送してください。
  • 分割代入の後、error は生の E です。error ではなく Err(error) を返してください。
  • Result 全体を保持している場合は、そのまま返してください。if (result.error !== null) return result。
  • error !== null が確実な判別方法です。Err(null) や Err(undefined) を構築しないでください。
  • catch 分岐が常に Ok<T> を返し、推論される型が Ok<T> に絞り込まれる場合は、データのみの分割代入が適切です。
  • 成功データが不要な場合は、エラーのみの分割代入が適切です。使わないフィールドまで分割代入するのではなく、起こりうるすべての Err を処理することが重要です。

成功パスを直線的に保てるよう、早めにガード節を設けてください。

Promise の所有者を明確にする

tryAsync は Promise を返します。その所有者を明示的に選んでください。

  • この関数内で Result を調べる場合は、await を使用します。
  • 呼び出し元に Promise<Result<...>> の所有を委ねる場合は、return tryAsync(...) を使用します。
  • 単独の void tryAsync(...) は使用しないでください。通常の失敗では Err を含む形で Promise が fulfilled になるため、Promise の reject ハンドラーではその失敗を検出できません。ベストエフォートの操作にも、Result を await し、エラー分岐を明示的にログに記録するか無視する、非同期処理の所有者が必要です。
  • UI の fire-and-forget コードでは、Result を含む形で fulfilled になる Promise を破棄する前に、結果の表示処理を付与します。例:void save().then((result) => toastOnError(result, 'Save failed'))。Promise が reject される可能性がある場合は、先にその reject を変換するか、catch してください。

契約を表現する場合はネイティブの try-catch を維持する

従来の try-catch が適切なのは、次のような場合です。

  • finally ブロックでクリーンアップを行う場合。
  • ジェネレーターで失敗を Result として返すのではなく、yield する必要がある場合。
  • フレームワークの境界で、例外を必要なレスポンス形式に直接変換する場合。
  • 既知の例外を 1 つだけ捕捉し、それ以外はすべて再スローする場合。
  • 周囲のパブリック API が意図的に例外をスローする場合。

未知のプログラミングエラーを汎用的なドメインエラーに変換しないでください。すべての throw を Err に変換すると、誤解を招く再試行可能なエラーの背後にバグが隠れてしまうおそれがあります。

最終確認

  1. 関数の throw と Result のどちらを使うかという契約が明確です。
  2. ラッパーが一貫した失敗の意味を扱っています。
  3. 捕捉した値が、型付きエラー、意図的なフォールバック、または選択的な再スローに変換されています。
  4. 発生し得る Err 分岐がすべて、名前付きのベストエフォート処理の所有者によって処理、転送、ログ記録、表示、または明示的に無視されています。
  5. 未知のバグが、適切なクラッシュ処理またはフレームワークのエラー境界に到達します。

コメント