mcpField
E
error-handling

error-handling

github.com/metarhia開発6038 回インストール12 週間前に更新
リポジトリ

error-handling について

開発者向けのエラー処理スキルです。JavaScriptの同期・非同期処理やレイヤー間の伝播、復旧、プロセス終了の指針を扱います。

主な機能

  1. 01プログラミングエラーと運用エラーの区別
  2. 02同期・非同期例外の処理
  3. 03DomainErrorによる業務エラー表現
  4. 04再試行とプロセスレベルのハンドラー
  5. 05グレースフルシャットダウン

活用シーン

  1. 01APIの業務エラーを構造化コードに変換
  2. 02一時的なネットワーク障害の再試行
  3. 03未捕捉例外や未処理の拒否の調査

インストール

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

$npx skills add https://github.com/metarhia/metaskills --skill error-handling

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

詳細

エラー処理

エラーの分類

  • プログラミングエラー:バグ(TypeError、ReferenceError、アサーション失敗など)。コードを修正してください。捕捉して処理を続行しないでください
  • 運用エラー:想定される失敗(ネットワークタイムアウト、ファイルが見つからない、入力が無効など)。復旧、エスカレーション、ユーザーへの通知、ログ記録などにより適切に処理してください

同期エラー処理

try {
  const result = parse(input);
  return result;
} catch (error) {
  console.error({ error });
  return defaultValue;
}

非同期エラー処理

try {
  const data = await fetchData(url);
  return data;
} catch (error) {
  if (error.code === 'ECONNREFUSED') return fallback();
  throw err;
}

DomainError

API レイヤーで使用する、コードを含む構造化されたビジネスエラーです。

({
  method: async ({ email }) => {
    const user = await domain.user.findByEmail(email);
    if (!user) return new DomainError('ENOTFOUND');
    return user;
  },
  errors: { ENOTFOUND: 'User not found' },
});

ドメインレイヤーでは通常のエラーをスローし、必要に応じて API レイヤーで DomainError に変換します。

レイヤー間でのエラー伝播

domain throws Error → API catches → returns DomainError(code) → client gets structured error

内部エラーの詳細をクライアントに公開しないでください。既知のエラーコードまたは一般的なエラーに変換してください。

規約

  • プログラミングエラーと運用エラーを区別し、復旧するのは運用エラーのみとします
  • API メソッドでのビジネス検証には DomainError を使用し、プログラミングバグにはエラーをスローします
  • エラーを黙って握りつぶさないでください。必ずログに記録するか、伝播させてください
  • グレースフルシャットダウンを実装してください。新しい接続の受付を停止し、既存の接続を処理し、リソースを解放します
  • 一時的な障害(ネットワーク、DB 接続)には、指数バックオフを使用して再試行してください
  • プロセスレベルで uncaughtException と unhandledRejection の両方を必ず処理してください

再試行パターン

const retry = async (fn, { attempts = 3, delay = 1000 } = {}) => {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === attempts - 1) throw err;
      await new Promise((r) => setTimeout(r, delay * (i + 1)));
    }
  }
};

プロセスレベルのハンドラー

process.on('uncaughtException', (error) => {
  console.error('Uncaught:', error);
  process.exit(1);
});

process.on('unhandledRejection', (reason) => {
  console.error('Unhandled rejection:', reason);
});

uncaughtException が発生したらログに記録して終了してください(状態が破損している可能性があります)。unhandledRejection をログに記録し、修正すべきバグとして扱ってください。

グレースフルシャットダウン

接続を追跡し、終了前に処理を完了してください。

const connections = new Map();

server.on('connection', (conn) => {
  const res = null;
  connections.set(conn, res);
  conn.on('close', () => connections.delete(conn));
});

const shutdown = () => {
  server.close(() => {
    freeResources();
    process.exit(0);
  });
  for (const [conn, res] of connections) {
    if (res) res.end('Server shutting down');
    conn.destroy();
  }
};

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

コメント