본문으로 건너뛰기
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(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

エラーハンドリング基準

サイレントエラーゼロトレランスを原則とし、すべてのエラーを適切に分類・処理・記録するためのスキル。 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 / 1,000자

이 양식은 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의 스킬 모두 보기

이 스킬의 문제 신고