# Error Handling

> アプリケーションのエラーハンドリング設計と実装。 例外階層、エラーレスポンス、ログ戦略、リトライ処理。 エラー処理実装、例外設計、障害対応時に使用。

- Skill: `atomic-kanta-sasaki/error-handling` (Agent Skill)
- Install (CLI): `npx skillmds@latest add atomic-kanta-sasaki/error-handling`
- Raw SKILL.md: https://api.skillmd.com/api/skills/atomic-kanta-sasaki/error-handling/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: atomic-kanta-sasaki (https://skillmd.com/u/atomic-kanta-sasaki)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/atomic-kanta-sasaki/error-handling

---


# Error Handling Skill

## Overview
堅牢なエラーハンドリングパターンを提供します。

## Error Hierarchy

```
BaseError
├── ValidationError      # 入力検証エラー
├── AuthenticationError  # 認証エラー
├── AuthorizationError   # 認可エラー
├── NotFoundError        # リソース未発見
├── ConflictError        # 競合（重複など）
├── ExternalServiceError # 外部サービスエラー
│   ├── DatabaseError
│   ├── CacheError
│   └── ThirdPartyAPIError
└── InternalError        # 内部エラー
```

## Implementation

### Python
```python
from dataclasses import dataclass
from typing import Optional, List
from enum import Enum

class ErrorCode(Enum):
    VALIDATION_ERROR = "VALIDATION_ERROR"
    AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR"
    AUTHORIZATION_ERROR = "AUTHORIZATION_ERROR"
    NOT_FOUND = "NOT_FOUND"
    CONFLICT = "CONFLICT"
    EXTERNAL_SERVICE_ERROR = "EXTERNAL_SERVICE_ERROR"
    INTERNAL_ERROR = "INTERNAL_ERROR"

@dataclass
class ErrorDetail:
    field: str
    message: str

class AppError(Exception):
    def __init__(
        self,
        code: ErrorCode,
        message: str,
        details: Optional[List[ErrorDetail]] = None,
        cause: Optional[Exception] = None
    ):
        self.code = code
        self.message = message
        self.details = details or []
        self.cause = cause
        super().__init__(message)
    
    def to_dict(self):
        return {
            "error": {
                "code": self.code.value,
                "message": self.message,
                "details": [
                    {"field": d.field, "message": d.message}
                    for d in self.details
                ]
            }
        }

class ValidationError(AppError):
    def __init__(self, message: str, details: List[ErrorDetail]):
        super().__init__(ErrorCode.VALIDATION_ERROR, message, details)

class NotFoundError(AppError):
    def __init__(self, resource: str, identifier: str):
        super().__init__(
            ErrorCode.NOT_FOUND,
            f"{resource} not found: {identifier}"
        )
```

### TypeScript
```typescript
enum ErrorCode {
  VALIDATION_ERROR = 'VALIDATION_ERROR',
  AUTHENTICATION_ERROR = 'AUTHENTICATION_ERROR',
  AUTHORIZATION_ERROR = 'AUTHORIZATION_ERROR',
  NOT_FOUND = 'NOT_FOUND',
  CONFLICT = 'CONFLICT',
  EXTERNAL_SERVICE_ERROR = 'EXTERNAL_SERVICE_ERROR',
  INTERNAL_ERROR = 'INTERNAL_ERROR',
}

interface ErrorDetail {
  field: string;
  message: string;
}

class AppError extends Error {
  constructor(
    public readonly code: ErrorCode,
    message: string,
    public readonly details: ErrorDetail[] = [],
    public readonly cause?: Error
  ) {
    super(message);
    this.name = 'AppError';
  }

  toJSON() {
    return {
      error: {
        code: this.code,
        message: this.message,
        details: this.details,
      },
    };
  }
}
```

## Global Error Handler (Express.js)

```typescript
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  // ログ出力
  logger.error({
    error: err.message,
    code: err.code,
    stack: err.stack,
    requestId: req.id,
    path: req.path,
    method: req.method,
  });

  // AppErrorの場合
  if (err instanceof AppError) {
    const statusCode = errorCodeToStatus(err.code);
    return res.status(statusCode).json(err.toJSON());
  }

  // 予期しないエラー
  return res.status(500).json({
    error: {
      code: 'INTERNAL_ERROR',
      message: 'An unexpected error occurred',
    },
  });
};

function errorCodeToStatus(code: ErrorCode): number {
  const mapping: Record<ErrorCode, number> = {
    VALIDATION_ERROR: 400,
    AUTHENTICATION_ERROR: 401,
    AUTHORIZATION_ERROR: 403,
    NOT_FOUND: 404,
    CONFLICT: 409,
    EXTERNAL_SERVICE_ERROR: 502,
    INTERNAL_ERROR: 500,
  };
  return mapping[code] || 500;
}
```

## Retry Strategy

```typescript
interface RetryConfig {
  maxRetries: number;
  baseDelayMs: number;
  maxDelayMs: number;
  retryableErrors: ErrorCode[];
}

async function withRetry<T>(
  operation: () => Promise<T>,
  config: RetryConfig
): Promise<T> {
  let lastError: Error;
  
  for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      lastError = error;
      
      if (!isRetryable(error, config.retryableErrors)) {
        throw error;
      }
      
      if (attempt < config.maxRetries) {
        const delay = Math.min(
          config.baseDelayMs * Math.pow(2, attempt),
          config.maxDelayMs
        );
        await sleep(delay + Math.random() * 100); // jitter
      }
    }
  }
  
  throw lastError;
}
```

## Logging Strategy

| Level | 用途 | 例 |
|-------|------|-----|
| ERROR | 即時対応が必要 | 決済失敗、データ不整合 |
| WARN | 注意が必要 | リトライ発生、レート制限 |
| INFO | 重要なイベント | ユーザー登録、注文完了 |
| DEBUG | 開発用詳細 | リクエスト/レスポンス |

```typescript
// 構造化ログ
logger.error({
  event: 'payment_failed',
  userId: user.id,
  orderId: order.id,
  amount: order.total,
  error: error.message,
  errorCode: error.code,
  provider: 'stripe',
  requestId: context.requestId,
});
```

