CLAUDE LABEN
MCP — 7月28日のMCP仕様リリース候補でMcp-Session-Idヘッダーが廃止されステートレス化します。リモートMCPサーバーはスティッキーセッションなしで運用できますAPPS — 同じ仕様でMCP Apps(サーバー側でレンダリングするUI)と、長時間処理向けのTasks拡張が加わりますMEMORY — Python 0.116.0・TypeScript 0.110.0・Go 1.56.0など各SDKが、メモリストア呼び出しにagent-memory-2026-07-22ヘッダーを送るようになりましたSPILL — agent_toolsetやMCPツールの出力が10万文字を超えると自動でサンドボックス内のファイルへ退避され、モデルには切り詰めたプレビューが渡りますBG — 2分を超えるMCPツール呼び出しが自動でバックグラウンドへ移り、セッションを止めなくなりました。CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますRESUME — エージェントビューで/resumeを打つと過去セッションのピッカーが開き、選んだものをバックグラウンドセッションとして再開できますMCP — 7月28日のMCP仕様リリース候補でMcp-Session-Idヘッダーが廃止されステートレス化します。リモートMCPサーバーはスティッキーセッションなしで運用できますAPPS — 同じ仕様でMCP Apps(サーバー側でレンダリングするUI)と、長時間処理向けのTasks拡張が加わりますMEMORY — Python 0.116.0・TypeScript 0.110.0・Go 1.56.0など各SDKが、メモリストア呼び出しにagent-memory-2026-07-22ヘッダーを送るようになりましたSPILL — agent_toolsetやMCPツールの出力が10万文字を超えると自動でサンドボックス内のファイルへ退避され、モデルには切り詰めたプレビューが渡りますBG — 2分を超えるMCPツール呼び出しが自動でバックグラウンドへ移り、セッションを止めなくなりました。CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますRESUME — エージェントビューで/resumeを打つと過去セッションのピッカーが開き、選んだものをバックグラウンドセッションとして再開できます
記事一覧/API & SDK
API & SDK/2026-04-08上級

Claude API × Webhook・非同期処理のエラーとリカバリー戦略

Claude API をWebhookや非同期処理に組み込む際のエラーパターンを網羅。タイムアウト・重複処理・冪等性・デッドレターキューまで、本番環境で求められるリカバリー戦略を実装例つきで徹底解説します。

API27Webhook4非同期処理4エラーハンドリング6本番環境5冪等性4

プレミアム記事

Claude API を Webhook や非同期処理パイプラインに組み込むと、同期呼び出しとは異なるエラーパターンが次々と現れます。「Webhook が届いたのに処理が実行されない」「同じメッセージが2回処理された」「Claude が応答する前にジョブがタイムアウトした」――こういった問題は、適切なリカバリー戦略があれば確実に防げます。

非同期処理における Claude API のエラー分類

Claude API を非同期で呼び出す際のエラーは、大きく4カテゴリに分類できます。

一時的なエラー(Transient Errors):自動リトライで解決できるもの。429 Too Many Requests(レート制限)、500/502/503/529 サーバーエラー、ネットワークタイムアウトなどがこれに当たります。

恒久的なエラー(Permanent Errors):リトライしても解決しないもの。400 Bad Request(リクエスト形式の誤り)、401 Unauthorized(APIキー無効)、404 Not Found(モデル名のタイポ等)が該当します。

ビジネスロジックエラー:Claude の応答内容に起因するもの。期待した構造化データが返ってこない、コンテンツポリシーによる refusal、不完全な生成(途中で止まった)などが挙げられます。

インフラエラー:処理基盤に起因するもの。Webhook 配信の失敗、キューのオーバーフロー、処理時間超過によるワーカーの強制終了などがあります。

この4カテゴリを意識することで、どの層で対処すべきかが明確になります。

Webhook 配信エラーの3大パターンと対処

パターン1:配信タイムアウト(At-least-once 配信の落とし穴)

多くの Webhook サービスは「少なくとも1回配信する(at-least-once delivery)」保証を持ちます。エンドポイントが一定時間内にレスポンスを返さないと、再送が発生します。Claude API の呼び出しに時間がかかる場合、これが重複処理の原因になります。

悪い実装例(Webhook ハンドラーで直接 Claude API を呼ぶ):

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    data = request.json
    # ❌ Webhookハンドラーで直接呼び出すと最大30秒かかり、
    # Webhookサービスが再送(重複処理)を引き起こす
    result = call_claude_api(data['message'])
    return {'status': 'ok', 'result': result}

良い実装例(即時受付→非同期処理):

from flask import Flask, request
import redis
import json
import uuid
 
app = Flask(__name__)
redis_client = redis.Redis(host='localhost', port=6379)
 
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    data = request.json
    job_id = str(uuid.uuid4())
 
    # 冪等キーで重複チェック
    idempotency_key = data.get('idempotency_key') or data.get('event_id')
    if idempotency_key:
        existing = redis_client.get(f'processed:{idempotency_key}')
        if existing:
            return {'status': 'already_processed', 'job_id': existing.decode()}
 
    # ✅ キューに積んで即座に202を返す
    redis_client.rpush('claude_jobs', json.dumps({
        'job_id': job_id,
        'idempotency_key': idempotency_key,
        'payload': data
    }))
 
    if idempotency_key:
        redis_client.setex(f'processed:{idempotency_key}', 86400, job_id)
 
    return {'status': 'accepted', 'job_id': job_id}, 202

パターン2:重複処理と冪等性の確保

ネットワーク障害や再送ポリシーにより、同じ Webhook が複数回届くことは避けられません。Claude API の呼び出しコストを考えると、重複処理は財務的にも機能的にも問題になります。

冪等性を保証するワーカー実装:

import anthropic
import redis
import json
import logging
 
client = anthropic.Anthropic()
redis_client = redis.Redis(host='localhost', port=6379)
logger = logging.getLogger(__name__)
 
def process_job(job_data: dict) -> dict:
    """冪等性を保証するジョブ処理"""
    job_id = job_data['job_id']
    idempotency_key = job_data.get('idempotency_key')
 
    # 処理結果キャッシュの確認
    result_key = f'result:{idempotency_key or job_id}'
    cached_result = redis_client.get(result_key)
    if cached_result:
        logger.info(f"キャッシュ済み結果を返します: {job_id}")
        return json.loads(cached_result)
 
    # 処理中フラグのセット(重複実行防止)
    lock_key = f'lock:{idempotency_key or job_id}'
    lock_acquired = redis_client.set(lock_key, '1', ex=300, nx=True)
    if not lock_acquired:
        logger.warning(f"既に処理中: {job_id}")
        return {'status': 'in_progress'}
 
    try:
        response = call_claude_with_retry(job_data['payload'])
        result = {'status': 'success', 'job_id': job_id, 'response': response}
        # 結果をキャッシュ(24時間)
        redis_client.setex(result_key, 86400, json.dumps(result))
        return result
    except Exception as e:
        logger.error(f"処理失敗: {job_id}, エラー: {e}")
        raise
    finally:
        redis_client.delete(lock_key)

パターン3:大量受信時のバックプレッシャー制御

Claude API にはレート制限があります。Webhook が大量に届いた際に全リクエストを同時に処理しようとすると、429 エラーが頻発します。Token Bucket アルゴリズムでスロットリングを実装しましょう。

import time
import threading
 
class TokenBucket:
    """Claude APIのレート制限に合わせたトークンバケット"""
 
    def __init__(self, tokens_per_minute: int):
        self.capacity = tokens_per_minute
        self.tokens = tokens_per_minute
        self.last_refill = time.time()
        self.lock = threading.Lock()
 
    def consume(self, tokens: int = 1) -> bool:
        with self.lock:
            now = time.time()
            elapsed = now - self.last_refill
            self.tokens = min(self.capacity, self.tokens + elapsed * (self.capacity / 60))
            self.last_refill = now
            if self.tokens >= tokens:
                self.tokens -= tokens
                return True
            return False
 
    def wait_and_consume(self, timeout: float = 300) -> bool:
        start = time.time()
        while time.time() - start < timeout:
            if self.consume():
                return True
            time.sleep(0.1)
        return False
 
rate_limiter = TokenBucket(tokens_per_minute=60)
 
def rate_limited_worker():
    while True:
        job = redis_client.blpop('claude_jobs', timeout=1)
        if not job:
            continue
        job_data = json.loads(job[1])
        if not rate_limiter.wait_and_consume():
            redis_client.rpush('claude_jobs_dead', json.dumps({
                **job_data,
                'failure_reason': 'rate_limit_timeout',
                'failed_at': time.time()
            }))
            continue
        try:
            process_job(job_data)
        except Exception as e:
            handle_job_failure(job_data, e)

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

この記事の続きを読む

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

この記事で得られること
Webhook配信エラーの3大パターンと自動リトライ設計のベストプラクティス
冪等キーを使った重複処理防止の実装方法(Python例付き)
デッドレターキュー・サーキットブレーカー・Graceful Degradationの実践設計
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

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

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

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

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

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

関連記事

API & SDK2026-03-09
Claude API エラーハンドリングとリトライ戦略
Claude API のエラーコード、リトライ戦略、レート制限の対処法を解説。本番環境で安定した API 連携を実現するためのベストプラクティスを紹介します。
API & SDK2026-07-24
メモリストアの棚卸しが半分しか返らない — path_prefix セグメント一致への移行
Managed Agents のメモリ一覧が agent-memory-2026-07-22 で仕様変更されました。順序固定・depth 厳格化・path_prefix のセグメント一致。棚卸しスクリプトが静かに件数を落とした実例と、破壊的変更に耐えるアクセス層の設計をまとめます。
API & SDK2026-07-14
ユーザーに届くAI生成の短文を、公開前に二段ゲートで止める
アプリでユーザーに配信するAI生成の短文に、決定的ルール層とClaude分類層の二段公開ゲートを組む設計です。fail-closed・生成時検疫・コスト設計まで、実装コード付きで整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →