本文へ移動
cccskills
無料GitHub で公開

error-handling-standards

Enforces error handling standards: silent error prohibition, custom error class hierarchy (AppError base with category never-fallback / transient / permanent, cause preservation, ValidationError / UnauthorizedError / UpstreamError etc. — canonical definitions in PATTERNS.md), external-boundary normalization (normalizeExternalError), Result pattern (Result.ok/Result.fail), proper try-catch with error type checking, structured error logging with a single Logger contract, fallback prohibition categories (isNeverFallback), and HTTP status code mapping. Use when implementing error handling, reviewing catch blocks, or designing error responses.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md15.2 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

エラーハンドリング基準

サイレントエラーゼロトレランスを原則とし、すべてのエラーを適切に分類・処理・記録するためのスキル。 PATTERNS.md のセクション 3, 9 および FALLBACK.md で定義されたパターンを適用する。

1. サイレントエラーの禁止

以下のパターンはすべて禁止:

// ❌ 空の catch ブロック
try {
  doSomething();
} catch (e) {}

// ❌ console.log のみでエラーを握りつぶし
try {
  doSomething();
} catch (e) {
  console.log(e);
}

// ❌ エラーを無視して null/undefined を返す
try {
  return fetchData();
} catch (e) {
  return null;
}

// ❌ 汎用的すぎるエラーメッセージ
throw new Error("Something went wrong");

すべての catch ブロックは、エラーの記録、再スロー、またはResult.fail での返却のいずれかを行うこと。

例外: 環境分岐付きフォールバック(セクション8参照)は本番環境のみで許容される。無条件のフォールバックは禁止。

2. カスタムエラークラス階層

プロジェクトでは AppError を基底クラスとしたエラー階層を使用する。定義の正典は PATTERNS.md「エラーハンドリング」(本ファイルには基底クラスと 1 例だけを載せ、サブクラスの全一覧は複製しない — 複製は必ずドリフトする):

// options.cause で元エラーを保持する(ES2022 Error.cause。tsconfig の lib に ES2022 が必要)
type AppErrorOptions = { cause?: unknown };

// 分類。フォールバック可否・再試行可否は statusCode から推測せず、各サブクラスが宣言する
type ErrorCategory = "never-fallback" | "transient" | "permanent";

abstract class AppError extends Error {
  abstract readonly category: ErrorCategory;
  constructor(
    message: string,
    public readonly code: string,
    public readonly statusCode: number,
    options?: AppErrorOptions,
  ) {
    super(message, options);
    this.name = this.constructor.name;
  }
}

// バリデーション詳細(any を使わない / MASTER.md)。PATTERNS.md と同じ形状
interface ValidationDetail {
  field: string;
  message: string;
  constraint?: string;
}

// サブクラスの例(他の NotFoundError / ForbiddenError / ConflictError / UnauthorizedError /
// SecurityError / InternalError / UpstreamError / UpstreamRejectedError は PATTERNS.md)
// HTTP_STATUS は PATTERNS.md「エラーハンドリング」で定義済み(AppError と同じモジュールから import)
class ValidationError extends AppError {
  readonly category: ErrorCategory = "never-fallback";
  constructor(
    message: string,
    public readonly details: readonly ValidationDetail[],
    options?: AppErrorOptions,
  ) {
    super(message, "VALIDATION_ERROR", HTTP_STATUS.BAD_REQUEST, options);
  }
}

3. エラーコードと HTTP ステータスコード

エラークラスエラーコードHTTP ステータス分類用途
ValidationErrorVALIDATION_ERROR400never-fallback入力バリデーション失敗
UnauthorizedErrorUNAUTHORIZED401never-fallback未認証
ForbiddenErrorFORBIDDEN403never-fallback権限不足
SecurityErrorSECURITY_VIOLATION403never-fallback署名不一致・改ざん検知
NotFoundErrorNOT_FOUND404permanentリソース未検出
ConflictErrorCONFLICT409never-fallback重複・競合
InternalErrorINTERNAL_ERROR500permanent予期しない内部エラー
UpstreamErrorUPSTREAM_UNAVAILABLE502transient外部サービスの一時障害(再試行可)
UpstreamRejectedErrorUPSTREAM_REJECTED502never-fallback外部サービスによる恒久的な拒否(自コードの要求誤り。本番でフォールバックしない)

すべて PATTERNS.md「エラーハンドリング」で定義済み。新しいエラー種別が必要な場合は、必ず AppError を継承し category を宣言して作成する(宣言しないとコンパイルエラーになる)。

4. Result パターン

ビジネスロジックでは例外スローではなく Result パターンを優先する:

// Result 型
type Result<T> = { ok: true; value: T } | { ok: false; error: AppError };

const Result = {
  ok: <T>(value: T): Result<T> => ({ ok: true, value }),
  fail: <T>(error: AppError): Result<T> => ({ ok: false, error }),
};

// 使用例
async function processUser(userId: string): Promise<Result<User>> {
  try {
    const user = await userRepository.findById(userId);
    if (!user) {
      return Result.fail(new NotFoundError("User not found"));
    }

    const processed = await processUserData(user);
    return Result.ok(processed);
  } catch (error) {
    const err = error instanceof Error ? error : new Error(String(error));
    logger.error("Failed to process user", err, { userId });

    if (error instanceof AppError) {
      return Result.fail(error);
    }

    return Result.fail(new InternalError("Processing failed", { cause: err }));
  }
}

Result パターンのメリット:

  • 呼び出し側がエラーハンドリングを忘れない(型で強制)
  • 正常系と異常系が型レベルで明確に区別される
  • try-catch のネストが減りコードが読みやすくなる

5. Try-Catch のベストプラクティス

// ✅ 良い例: エラー型に応じた具体的な処理
try {
  await riskyOperation();
} catch (error) {
  if (error instanceof ValidationError) {
    return Result.fail(error); // そのまま返却
  }
  if (error instanceof NotFoundError) {
    // warn の meta に Error 実体は入れない(Logger 規約 / PATTERNS.md §9)
    logger.warn("Resource not found", { code: error.code });
    return Result.fail(error);
  }
  // 未知のエラーは InternalError でラップし、cause で元エラーを保持する
  const err = error instanceof Error ? error : new Error(String(error));
  logger.error("Unexpected error", err);
  return Result.fail(
    new InternalError("Unexpected error occurred", { cause: err }),
  );
}

// ❌ 悪い例: 汎用的な catch のみ
try {
  await riskyOperation();
} catch (error) {
  throw new Error("Failed"); // 元のエラー情報が失われる
}

// ✅ ラップするなら cause で元エラーを保持する
try {
  await riskyOperation();
} catch (error) {
  throw new InternalError("Failed", { cause: error });
}

ルール:

  • instanceof でエラー型をチェック
  • 具体的なエラーから順に処理
  • 未知のエラーは InternalError でラップして再スロー({ cause } で元エラーを保持)
  • 元のエラー情報は必ずログに記録
  • 外部 SDK / HTTP クライアントのエラーは境界で normalizeExternalError()(PATTERNS.md)により AppError へ正規化する

6. 構造化エラーログ

エラーログは JSON 形式で構造化し、必要なコンテキストを含めること。呼び出し規約はテンプレート全体で 1 つで、**正典(interface Logger)と実装例(JsonLogger)は PATTERNS.md §9「ログパターン」**にある。ここには複製せず、規約だけを示す:

  • error(message, error, meta?) — 第 2 引数は Error 型。catch 変数(unknown)は正規化してから渡す
  • warn(message, meta?) / info(message, meta?) — meta は構造化コンテキスト。Error 実体は入れない(name / code だけ載せる)
// 使用例
try {
  await riskyOperation(userId);
} catch (error) {
  const err = error instanceof Error ? error : new Error(String(error));
  logger.error("Failed to process user", err, {
    userId,
    operation: "riskyOperation",
    requestId: req.headers["x-request-id"],
  });
}

ログの必須フィールド:

  • level: エラーレベル(error / warn / info)
  • message: 人間が読めるエラー説明
  • error.name: エラークラス名
  • error.message: エラーメッセージ
  • error.stack: スタックトレース
  • timestamp: ISO 8601 形式

禁止事項:

  • 個人情報(パスワード、メールアドレス等)をログに含めない
  • スタックトレースをユーザー向けレスポンスに含めない

7. チェックリスト

エラーハンドリングのコードレビュー時に確認する項目:

  • 空の catch ブロックがないか
  • すべてのエラーが適切にログ記録されているか
  • カスタムエラークラス(AppError 継承)を使用しているか
  • エラーコードと HTTP ステータスが正しくマッピングされているか
  • Result パターンが適切に使用されているか
  • instanceof でエラー型をチェックしているか
  • 未知のエラーが InternalError でラップされているか
  • ログに個人情報が含まれていないか
  • ユーザー向けレスポンスにスタックトレースが露出していないか
  • フォールバック処理が環境分岐されているか(開発時スロー / 本番時フォールバック)
  • AI生成のtry-catch + デフォルト値返却パターンが残っていないか

8. AI生成コードのフォールバックアンチパターン

包括的なフォールバック戦略(階層モデル・レイヤー別パターン含む)は FALLBACK.md を参照。

AI(Claude Code, Copilot, Cursor等)は try-catch + デフォルト値返却を自動挿入する傾向がある。 このパターンは開発中のバグを隠蔽し、本番で初めて問題が発覚するリスクを生む。

検出すべきアンチパターン

// ❌ パターン1: 空 catch + デフォルト値
try {
  return await fetchData();
} catch {
  return defaultValue;
}

// ❌ パターン2: エラー無視 + 空配列/空文字
try {
  return await getItems();
} catch {
  return [];
}

// ❌ パターン3: catch 内で console.log のみ + フォールバック
try {
  return await getData();
} catch (e) {
  console.log(e);
  return fallbackData;
}

// ❌ パターン4: Promise.catch() でサイレントフォールバック
const data = await fetchData().catch(() => defaultValue);

修正パターン

フォールバックが必要な場合は、fallbackInProdOnly() ユーティリティ(推奨)または環境分岐を使用する:

// ✅ 推奨: ユーティリティを使う(禁止カテゴリの判定を内蔵している)。
// 引数は AppError に限定されるので、HTTP 境界の生エラーは normalizeExternalError() を通す
try {
  return await fetchData();
} catch (error) {
  return fallbackInProdOnly(defaultValue, normalizeExternalError(error), {
    operation: "fetchData",
  });
}

// △ インライン環境分岐(カスタムログが必要な場合のみ)
// 素の環境分岐は「フォールバック禁止カテゴリ」の判定を持たないため、
// 認証・認可・バリデーション・データ整合性・セキュリティ・上流の恒久的な拒否のエラーが
// 本番で握りつぶされる。この形を使うなら禁止カテゴリの判定を必ず添える。
try {
  return await fetchData();
} catch (error) {
  const normalizedError = normalizeExternalError(error);
  logger.error("Failed to fetch data", normalizedError, {
    operation: "fetchData",
  });

  // 禁止カテゴリは環境に関係なく常にスロー(FALLBACK.md Section 1 / §4 の isNeverFallback)。
  // 判定を自前で再実装しない — 禁止カテゴリの定義が変わったときに取り残される
  if (isNeverFallback(normalizedError)) {
    throw normalizedError;
  }

  const env = process.env.NODE_ENV;
  if (env === "development" || env === "test") {
    throw normalizedError; // 開発時: バグを即座に検出
  }

  return defaultValue; // 本番時のみ: UX保護
}

レビュー時の判断基準

状況対応
catch 内でデフォルト値を返している環境分岐を追加するよう指摘
.catch(() => default) パターンtry-catch + 環境分岐に書き換え
認証/認可/バリデーション/データ整合性/セキュリティ/上流の恒久的な拒否のエラーにフォールバック環境問わずスローに修正
既に fallbackInProdOnly() を使用OK(ログ記録・エラー正規化を確認)
環境分岐のみ(禁止カテゴリの判定なし)禁止カテゴリの判定を追加するよう指摘
フォールバックが明示的にビジネス要件コメントで理由を明記させる

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

code-review

無料日本語概要

プロジェクトガイドラインへの準拠をチェックし、バグ、スタイル違反、コード品質問題を検出する。PRレビュー時に使用。

feel-flow/ai-spec-driven-development72026年9月14日 更新

Enforces project coding standards during code review: naming conventions (PascalCase for classes, camelCase for methods/variables, UPPER_SNAKE_CASE for constants, kebab-case for files), standard file structure, anti-magic-number policy, file size limits (500/800 lines), security patterns, and design patterns. Use when reviewing code, writing new code, or checking adherence to project conventions.

日本語の概要は準備中です。原文の説明を表示しています。

feel-flow/ai-spec-driven-development72026年9月14日 更新

code-simplification

無料日本語概要

機能を保持したまま不要な複雑性を排除し、コードの簡潔性と可読性を向上させる。30行超の関数や3段以上のネスト検出時に使用。

feel-flow/ai-spec-driven-development72026年9月14日 更新

comment-analysis

無料日本語概要

コードコメントの正確性、完全性、長期的な保守性を分析する。JSDoc、コメント、README等のドキュメントが変更された場合に使用。

feel-flow/ai-spec-driven-development72026年9月14日 更新

error-handler-hunt

無料日本語概要

サイレント失敗を検出し、堅牢なエラーハンドリングを確保する。PRレビューでのエラーハンドリング検査時に使用。

feel-flow/ai-spec-driven-development72026年9月14日 更新

Creates and reviews agent skills safely by enforcing deletion-prevention harness rules: no fixed absolute temp paths, no cwd-dependent cleanup, no wildcard deletes, no workspace-external file operations, no silent cleanup failures, and mandatory temp-root validation before any destructive command. Use when creating a new skill, updating an existing SKILL.md, or reviewing prompts/runbooks that include temp files, extraction, cleanup, PowerShell, bash, rm, del, Remove-Item, or archive expansion.

日本語の概要は準備中です。原文の説明を表示しています。

feel-flow/ai-spec-driven-development72026年9月14日 更新

feel-flow のスキルをすべて見る

このスキルの問題を報告する