◉CLAUDE LABEN
●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました
記事一覧/API & SDK
⬡ API & SDK/2026-05-31上級

Claude 非同期パイプラインで「毒メッセージ」を隔離する — デッドレターキュー設計の実装メモ

1件の壊れた入力がバッチ全体を止める「毒メッセージ」問題を、Cloudflare Queues のデッドレターキューで隔離する設計と実装。Claude API 特有の失敗パターンの切り分けまで実体験ベースでまとめます。

Claude API128非同期処理4Cloudflare Queuesデッドレターキュー本番運用36

✦ プレミアム記事

壁紙アプリの新作分を一括でタグ付けしようと、画像メタデータ 800 件を Claude にまとめて投げたことがあります。300 件ほど処理が進んだところでパイプラインが固まり、ログを追うと、たった 1 件の壊れた JSON がコンシューマを例外で落とし、その後ろの全件がリトライの渋滞に巻き込まれていました。原因の 1 件は、過去のエクスポート不具合で description フィールドに生のバイナリが混ざっていたものでした。

この「1 件の壊れた入力が、健全な残り全部を巻き添えにする」現象は、メッセージキューの世界で 毒メッセージ(poison message) と呼ばれています。同期的に for ループで API を叩いているうちは表面化しませんが、非同期キューに載せて本番運用を始めた瞬間、必ず一度はぶつかる落とし穴です。私が壁紙アプリのバックエンド処理と 4 サイトのコンテンツ生成パイプラインで実際に踏んだこの問題を、ここでは デッドレターキュー(DLQ) で隔離する設計として整理してみます。

毒メッセージがパイプライン全体を殺す仕組み

非同期パイプラインの基本は「キューにジョブを積み、コンシューマが 1 件ずつ取り出して Claude API を呼ぶ」という形です。健全に動いているうちは美しいのですが、コンシューマが処理に失敗したときの挙動が問題になります。

多くのキューは「失敗したメッセージは ack せず、一定時間後に再配信する」という at-least-once 配信を採ります。これは一時的なネットワーク断や 429(レート制限)には有効です。しかし、メッセージそのものが壊れていて 何度処理しても必ず失敗する 場合、同じメッセージが延々と再配信され続けます。

私の最初の実装では、この再配信が次の 3 つの形で残りの処理を巻き添えにしました。まず、壊れた 1 件がバッチの先頭に居座り、retryAll() を呼ぶ実装になっていたため、同じバッチに入っていた健全な 9 件も一緒に再送されました。次に、再送のたびに Claude API のトークンを消費するため、1 件の毒メッセージが静かに課金を膨らませました。最後に、リトライ回数の上限を設けていなかったため、毒メッセージがキューに永久に居座り、可視性タイムアウトの間スループットを食い続けました。

毒メッセージ対策の本質は、「直る失敗」と「直らない失敗」を切り分け、後者を健全なフローから物理的に追い出す ことです。

まず「直る失敗」と「直らない失敗」を切り分ける

すべての失敗を一律にリトライする実装は、毒メッセージを増幅させます。Claude API を呼ぶパイプラインでは、エラーを大きく 3 種類に分けて考えると整理できます。

一時的でリトライすべきもの(transient)は、429 レート制限、529 過負荷、500/503、ネットワークタイムアウトです。これらは時間を置けば直るので、指数バックオフでリトライする価値があります。

入力起因で何度やっても直らないもの(毒メッセージ)は、400 invalid_request(リクエスト本体が壊れている)、メッセージのデシリアライズ失敗、スキーマ検証で弾かれる構造化出力、max_tokens に対して入力が大きすぎてどうしても収まらないケースです。これらは即座に DLQ へ送るべきです。

判断が分かれるグレーゾーンもあります。stop_reason が max_tokens で途中で切れた応答、refusal で拒否された応答などです。これらは「リトライしても結果が変わりにくいが、入力の前処理を変えれば直る可能性がある」ため、回数を絞ったリトライののち DLQ へ送り、人間または別ロジックで再処理する設計が合っています。

この切り分けを 1 つの関数に閉じ込めておくと、コンシューマ本体がすっきりします。

// classify.ts — 失敗を「リトライ可 / 毒メッセージ」に分類する
import Anthropic from "@anthropic-ai/sdk";
 
export type Verdict = "retry" | "poison";
 
export function classifyError(err: unknown): Verdict {
  // SDK が投げる APIError には status が乗る
  if (err instanceof Anthropic.APIError) {
    const s = err.status;
    if (s === 429 || s === 529 || s === 500 || s === 503) return "retry";
    if (s === 400 || s === 422) return "poison"; // 入力が壊れている
    if (s === 401 || s === 403) return "retry";  // 鍵やIP制限。設定直しで回復
  }
  // ネットワーク系(abort / ETIMEDOUT など)は一時障害扱い
  const name = (err as Error)?.name ?? "";
  if (name === "AbortError" || /timeout|ECONNRESET|ETIMEDOUT/i.test(String(err))) {
    return "retry";
  }
  // JSON.parse 失敗やスキーマ違反は毒メッセージ
  if (err instanceof SyntaxError) return "poison";
  // 分類不能は安全側(リトライ)に倒す。回数上限で最終的に DLQ へ落ちる
  return "retry";
}

ポイントは、分類不能なものを「とりあえず毒メッセージ」にしない ことです。未知のエラーはリトライ側に倒し、後述する max_retries の上限で自然に DLQ へ落とします。こうすると、こちらが想定していなかった一時障害を、誤って毒メッセージとして捨ててしまう事故を防げます。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦リトライで直る一時障害と、何度送っても失敗する毒メッセージを stop_reason とエラー型で機械的に切り分ける判定ロジック
✦Cloudflare Queues の max_retries と dead_letter_queue を使い、壊れた1件でバッチ全体を止めない隔離パイプラインの実装コード(Before/After 付き)
✦DLQ に溜まったメッセージを原因別に分類し、安全に再処理(リドライブ)するための運用設計と、再処理時に二重課金を防ぐ冪等キーの付け方
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ API & SDK2026-05-24
Claude API のレスポンスを Cloudflare R2 にアーカイブする実装メモ — 監査・再現性・障害分析の3軸設計
Claude API のリクエストとレスポンスを Cloudflare R2 に保存して、監査・障害分析・本番リプレイの3つに使う設計を、Workers の waitUntil パターンと D1 メタデータインデックス込みで実装メモにまとめました。
⬡ API & SDK2026-05-04
Bun × Claude API で本番運用するエッジAIサービス — Node.js移行の判断軸と落とし穴を回避する実装パターン
Claude APIをBun上で本番運用するための実装ガイド。Node.jsからの移行判断軸・組み込みSQLite/WebSocketの活用・ストリーミング最適化・典型的な落とし穴の回避策まで、動作するコードと実測値で解説します。
⬡ API & SDK2026-04-30
Claude API で多言語翻訳 SaaS を本番運用する設計ガイド — 用語集・スタイル・ドメイン適応の実装
多言語翻訳SaaSをClaude APIで本番運用するための設計を通しで解説します。タグやプレースホルダーを失わせない前処理、用語集の注入と強制、スタイルガイドによるトーン制御、Few-shotでのドメイン適応、品質ゲートとコスト最適化まで、具体的なコードと運用ノウハウでまとめました。