跳到正文
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 ERRORS.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.5 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

エラーハンドリング基準

サイレントエラーゼロトレランスを原則とし、すべてのエラーを適切に分類・処理・記録するためのスキル。 ERRORS.md、PATTERNS.md のセクション 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 を基底クラスとしたエラー階層を使用する。定義の正典は ERRORS.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)。ERRORS.md と同じ形状
interface ValidationDetail {
  field: string;
  message: string;
  constraint?: string;
}

// サブクラスの例(他の NotFoundError / ForbiddenError / ConflictError / UnauthorizedError /
// SecurityError / InternalError / UpstreamError / UpstreamRejectedError / UnknownExternalError は ERRORS.md)
// HTTP_STATUS は ERRORS.md で定義(`./errors` から 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外部サービスによる恒久的な拒否(自コードの要求誤り。本番でフォールバックしない)
UnknownExternalErrorUNKNOWN_EXTERNAL_ERROR502never-fallback原因不明の外部エラー(推測で再試行・フォールバックしない)

すべて ERRORS.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) {
    logger.error("Failed to process user", error, { userId });

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

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

Result パターンのメリット:

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

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

// ✅ 良い例: 外部 SDK / HTTP の境界で正規化し、エラー型に応じて処理
try {
  await riskyOperation();
} catch (error) {
  const normalized = normalizeExternalError(error);
  if (normalized instanceof ValidationError) {
    return Result.fail(normalized); // そのまま返却
  }
  if (normalized instanceof NotFoundError) {
    // warn の meta に Error 実体は入れない(Logger 規約 / PATTERNS.md §9)
    logger.warn("Resource not found", { code: normalized.code });
    return Result.fail(normalized);
  }
  logger.error("External operation failed", error);
  return Result.fail(normalized);
}

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

// ✅ 外部境界で再スローするなら分類と元の cause を保持する
try {
  await riskyOperation();
} catch (error) {
  throw normalizeExternalError(error);
}

ルール:

  • instanceof でエラー型をチェック
  • 具体的なエラーから順に処理
  • 内部の不具合と確認したエラーは InternalError でラップして再スロー({ cause } で元の値を保持)。分類不能な外部エラーは normalizeExternalError() に渡し UnknownExternalError(never-fallback)にする
  • 元のエラー情報は必ずログに記録
  • 外部 SDK / HTTP クライアントのエラーは境界で normalizeExternalError()(ERRORS.md)により AppError へ正規化する

6. 構造化エラーログ

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

  • error(message, error, meta?) — 第 2 引数は unknown。元の値をそのまま渡し、PATTERNS.md §9 の serializeError で構造化する
  • warn(message, meta?) / info(message, meta?) — meta は構造化コンテキスト。Error 実体は入れない(name / code だけ載せる)
// 使用例
try {
  await riskyOperation(userId);
} catch (error) {
  logger.error("Failed to process user", error, {
    userId,
    operation: "riskyOperation",
    requestId: req.headers["x-request-id"],
  });
}

ログの必須フィールド:

  • level: エラーレベル(error / warn / info)
  • message: 人間が読めるエラー説明
  • error: serializeError() が返す構造化情報。Error は name / message / stack、非 Error は status / body または shape など、実際に取得できたフィールドを記録する(欠けた情報を作らない)
  • timestamp: ISO 8601 形式

禁止事項:

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

7. チェックリスト

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

  • 空の catch ブロックがないか
  • すべてのエラーが適切にログ記録されているか
  • カスタムエラークラス(AppError 継承)を使用しているか
  • エラーコードと HTTP ステータスが正しくマッピングされているか
  • Result パターンが適切に使用されているか
  • instanceof でエラー型をチェックしているか
  • 内部不具合は InternalError、分類不能な外部エラーは UnknownExternalError として区別し、元の cause を保持しているか
  • ログに個人情報が含まれていないか
  • ユーザー向けレスポンスにスタックトレースが露出していないか
  • フォールバック処理は明示的な production だけで許可し、禁止カテゴリは環境にかかわらずスローしているか
  • 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 !== "production") {
    throw normalizedError; // 未設定・未知の値・開発・テストは fail-fast
  }

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

レビュー時の判断基準

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

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

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

中文简介正在准备中。当前显示原文说明。

feel-flow/ai-spec-driven-development72026年10月18日 更新

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年10月18日 更新

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

中文简介正在准备中。当前显示原文说明。

feel-flow/ai-spec-driven-development72026年10月18日 更新

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

中文简介正在准备中。当前显示原文说明。

feel-flow/ai-spec-driven-development72026年10月18日 更新

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

中文简介正在准备中。当前显示原文说明。

feel-flow/ai-spec-driven-development72026年10月18日 更新

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年10月18日 更新

查看 feel-flow 的全部技能

报告此技能的问题