Claude Platform Docs
メッセージClaudeで構築

停止理由とフォールバック

各 stop_reason の値が何を意味するのか、そしてアプリケーションで切り捨て、ツール使用、一時停止されたターン、拒否をどのように処理するかを学びます。

すべての Messages API レスポンスには、Claude が生成を停止した理由を示す stop_reasonAPI リファレンスへのリンクを含むプレビューを表示できます — カードにフォーカスが移るまで Tab キーを押します。戻るには Escape キーを押します。 フィールドが含まれています。このフィールドを確認して、レスポンスをそのまま使用するか、会話を続けるか、リトライするか、別のモデルにフォールバックするかを判断してください。

完全なレスポンススキーマについては、Messages API リファレンスAPI リファレンスへのリンクを含むプレビューを表示できます — カードにフォーカスが移るまで Tab キーを押します。戻るには Escape キーを押します。を参照してください。

クイックリファレンス

値発生するタイミング対処方法
end_turnClaudeが自然にレスポンスを完了しました。レスポンスを使用します。
max_tokensレスポンスが max_tokens の上限に達しました。max_tokens を引き上げるか、レスポンスを継続します。
stop_sequenceClaude が stop_sequences のいずれかを出力しました。stop_sequence を読み取り、どれが発火したかを確認します。
tool_useClaude がツールを呼び出しています。ツールを実行して結果を返します。結果ブロックがまだないサーバーツール呼び出しは、後続のレスポンスで完了します。
pause_turnサーバーツールのループが反復回数の上限に達しました。アシスタントのコンテンツを送り返して継続します。
refusalClaude が応答を拒否しました。stop_details を読み取り、フォールバックモデルでリトライします。
model_context_window_exceededレスポンスがモデルのコンテキストウィンドウを使い切りました。レスポンスを切り捨てられたものとして扱います。

stop_reason フィールド

stop_reason フィールドは、成功したすべての Messages API レスポンスに含まれます。リクエストの処理における失敗を示すエラーとは異なり、stop_reason は Claude がレスポンスの生成を完了した理由を示します。

Example response
{
  "id": "msg_01234",
  "type": "message",
  "role": "assistant",
  "content": [
    {
      "type": "text",
      "text": "Here's the answer to your question..."
    }
  ],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "stop_details": null,
  "usage": {
    "input_tokens": 100,
    "output_tokens": 50
  }
}

停止理由の値

end_turn

最も一般的な停止理由です。Claudeが自然にレスポンスを完了したことを示します。

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
    # 完全なレスポンスを処理します
    for block in response.content:
        if block.type == "text":
            print(block.text)

max_tokens

リクエストで指定した max_tokensAPI リファレンスへのリンクを含むプレビューを表示できます — カードにフォーカスが移るまで Tab キーを押します。戻るには Escape キーを押します。 の上限に達したため、Claude が停止しました。

client = anthropic.Anthropic()
# トークン数を制限したリクエスト
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=10,
    messages=[{"role": "user", "content": "Explain quantum physics"}],
)

if response.stop_reason == "max_tokens":
    # レスポンスが途中で切り捨てられました
    print("Response was cut off at token limit")
    # 続きを取得するには、再度リクエストを送信することを検討してください

stop_sequence

Claude がカスタム停止シーケンスAPI リファレンスへのリンクを含むプレビューを表示できます — カードにフォーカスが移るまで Tab キーを押します。戻るには Escape キーを押します。のいずれかに遭遇しました。

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    stop_sequences=["END", "STOP"],
    messages=[{"role": "user", "content": "Generate text until you say END"}],
)

if response.stop_reason == "stop_sequence":
    print(f"Stopped at sequence: {response.stop_sequence}")

tool_use

Claudeが、あなたが実行する必要のあるツールを呼び出しています。

client = anthropic.Anthropic()
weather_tool = {
    "name": "get_weather",
    "description": "Get the current weather in a given location",
    "input_schema": {
        "type": "object",
        "properties": {
            "location": {"type": "string", "description": "City and state"},
        },
        "required": ["location"],
    },
}


def execute_tool(name, tool_input):
    """Run a tool and return the result."""
    return f"Weather in {tool_input.get('location', 'unknown')}: 72°F"


response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    tools=[weather_tool],
    messages=[{"role": "user", "content": "What is the weather in San Francisco?"}],
)

if response.stop_reason == "tool_use":
    # ツールを抽出して実行します
    for block in response.content:
        if block.type == "tool_use":
            result = execute_tool(block.name, block.input)
            # 最終的な応答のために結果を Claude に返します

tool_useレスポンスには、idに対応する結果ブロックがないserver_tool_useブロックが含まれることもあります。そのサーバーツール呼び出しは完了しておらず、このレスポンスにはその結果は含まれていません。一般的なケースでは、Claudeが同じ並列ツール呼び出しのグループ内でサーバーツールとクライアントツールの1つを呼び出した場合です。このときAPIは、先にクライアントツールを実行できるように、サーバーツールを実行せずに返します。この状態を示す他のマーカーはありません。各server_tool_useまたはmcp_tool_useブロックのidに一致する結果ブロックがあるかを確認して検出してください。

A mixed tool_use response
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "server_tool_use",
      "id": "srvtoolu_01HxbWnMRmbWyMfUtJKC45rA",
      "name": "web_search",
      "input": { "query": "example article" }
    },
    {
      "type": "tool_use",
      "id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "name": "run_command",
      "input": { "command": "uname -a" }
    }
  ]
}

継続は、レスポンス内のすべての tool_use ブロックに対して 1 つずつの tool_result ブロックからなるユーザーメッセージです(ツール呼び出しの処理を参照)。ただし、2 つの追加ルールがあります。そのメッセージには tool_result ブロック以外を含めてはならず、リクエストは同じ tools 配列を保持しなければなりません。待機中のサーバーツールを定義しなくなった再開リクエストは、メッセージが but no `web_search` tool was provided で終わる 400 で失敗します。API は結果をまだ開いているアシスタントのターンに付加し、遅延されたサーバーツールを実行し(一時停止されたコード実行の場合は再開し)、ターンを継続します。Claude が直接呼び出したサーバーツールの場合、次のレスポンスの content は、前のレスポンスの server_tool_use の id に応答する結果ブロックから始まります。

The follow-up user message
{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01PjgRJLbXrXEMZwDNYLnBqk",
      "content": "Linux demo-host 6.8.0-52-generic x86_64 GNU/Linux"
    }
  ]
}

そのユーザーメッセージの tool_result ブロックの後にテキストなど何かを追加すると、アシスタントのターンが終了します。Claude が直接呼び出したサーバーツールの場合、リクエストは未解決のサーバーツールを指す 400 invalid_request_error で失敗します:

`web_search` tool use with id `srvtoolu_01HxbWnMRmbWyMfUtJKC45rA` was found without a corresponding `web_search_tool_result` block

tool_result を省略したり、他のコンテンツの後に配置したりすると、代わりに標準の tool_use ids were found without tool_result blocks immediately after エラーでより早い段階で失敗します。Claude にさらに入力を与えるには、ターンが完了した後に別のユーザーメッセージとして送信してください。

pause_turn

ウェブ検索などのサーバーツールの実行中に、サーバー側のサンプリングループが反復回数の上限に達したときに返されます。デフォルトの上限はリクエストごとに10回です。

これが発生した場合、レスポンスには対応する結果ブロックのないserver_tool_useブロックが含まれることがあります。Claudeに処理を完了させるには、レスポンスをそのまま送り返して会話を続行してください。クライアントのtool_useブロックがあなたの応答を待っている状態のレスポンスのstop_reasonがpause_turnになることはありません。Claudeがあなたのツールを呼び出すために停止した場合、stop_reasonはtool_useであり、レスポンス自体ではなくクライアントのtool_resultブロックを送信して続行します。

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    tools=[{"type": "web_search_20250305", "name": "web_search"}],
    messages=[{"role": "user", "content": "Search for latest AI news"}],
)

if response.stop_reason == "pause_turn":
    # レスポンスを送り返して会話を続けます
    messages = [
        {"role": "user", "content": "Search for latest AI news"},
        {"role": "assistant", "content": response.content},
    ]
    continuation = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=4096,
        messages=messages,
        tools=[{"type": "web_search_20250305", "name": "web_search"}],
    )

refusal

Claude がレスポンスの生成を拒否しました。安全性分類器はこの停止理由をエラーではなく、通常の HTTP 200 レスポンスとして返します。

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "[Unsafe request]"}],
)

if response.stop_reason == "refusal":
    # Claude が応答を拒否しました
    print("Claude was unable to process this request")
    # リクエストの言い換えや修正を検討してください

拒否の場合、stop_detailsAPI リファレンスへのリンクを含むプレビューを表示できます — カードにフォーカスが移るまで Tab キーを押します。戻るには Escape キーを押します。オブジェクトは拒否を引き起こしたポリシーカテゴリを示します。カテゴリと拒否レスポンスの完全な形状については、拒否の例で説明しています。refusal以外のすべての停止理由では、stop_detailsはnullです。

拒否されたリクエストは、通常、別のClaudeモデルで再試行することでレスポンスを得られます。これは、Claude Fable 5.2、Claude Fable 5.1、Claude Fable 5、Claude Opus 5.5、Claude Opus 5、Claude Sonnet 5.5、およびClaude Haiku 5.5で機能します。拒否とフォールバックでは、サーバー側またはクライアントでその再試行を設定する方法を示しています。Claude Haiku 5.5にはサーバー側のフォールバックがないため、再試行はクライアント側のフォールバックで設定してください。Claude Haiku 5.5を除くこれらのいずれかのモデルからの再試行を自分で構築する場合、プロンプトキャッシュのコストを二重に支払わないようにする方法についてはフォールバッククレジットを参照してください。

model_context_window_exceeded

モデルのコンテキストウィンドウの上限に達したため、Claude が停止しました。これにより、正確な入力サイズを知らなくても、可能な最大トークン数をリクエストできます。

client = anthropic.Anthropic()

# できるだけ多く取得するため、最大トークン数でリクエストします
response = client.beta.messages.create(
    model="claude-opus-5-5",
    max_tokens=20000,  # Python SDK requires streaming for max_tokens above ~21k
    messages=[
        {"role": "user", "content": "Large input that uses most of context window..."}
    ],
)

if response.stop_reason == "model_context_window_exceeded":
    # max_tokens に達する前にレスポンスがコンテキストウィンドウの上限に達しました
    print("Response reached model's context window limit")
    # レスポンスは有効ですが、コンテキストウィンドウによって制限されています

停止理由を処理するためのベストプラクティス

常に stop_reason を確認する

レスポンス処理ロジックで stop_reason を確認することを習慣にしてください:

def handle_response(response):
    match response.stop_reason:
        case "tool_use":
            return handle_tool_use(response)
        case "max_tokens":
            return handle_truncation(response)
        case "model_context_window_exceeded":
            return handle_context_limit(response)
        case "pause_turn":
            return handle_pause(response)
        case "refusal":
            return handle_refusal(response)
        case _:
            # end_turn およびその他のケースを処理します
            return next(
                (block.text for block in response.content if block.type == "text"),
                "",
            )

切り捨てられたレスポンスを適切に処理する

トークン制限またはコンテキストウィンドウのためにレスポンスが切り捨てられた場合は、出力が不完全であることを読者がわかるように通知を追加してください。代わりにレスポンスが途切れた箇所から生成を継続するには、完全なレスポンスを確保するを参照してください。

def handle_truncated_response(response):
    text = next((block.text for block in response.content if block.type == "text"), "")
    if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
        if response.stop_reason == "max_tokens":
            note = "[Response truncated due to max_tokens limit]"
        else:
            note = "[Response truncated due to context window limit]"
        return f"{text}\n\n{note}"
    return text

pause_turn のリトライロジックを実装する

サーバーツールを使用する場合、サーバー側のサンプリングループが反復回数の上限(デフォルトは10)に達すると、APIがpause_turnを返すことがあります。これを処理するには、一時停止したレスポンスを変更せずにメッセージに追加し、リクエストを再度送信してください。レスポンスにcontainerが含まれている場合は、そのIDも送り返してください。IDを送り返さないと、次のリクエストで新しいコンテナが開始され、そのターンでそれまでに作成されたファイルは失われます。

client = anthropic.Anthropic()


def handle_server_tool_conversation(client, user_query, tools, max_requests=5):
    messages = [{"role": "user", "content": user_query}]
    container_id = None

    for _ in range(max_requests):
        response = client.messages.create(
            model="claude-opus-5-5",
            max_tokens=4096,
            messages=messages,
            tools=tools,
            container=container_id,
        )

        if response.stop_reason != "pause_turn":
            # 一時停止していない場合:レスポンスを返します
            return response

        # pause_turn:一時停止したレスポンスをassistantメッセージとして追加し、続行します
        messages.append({"role": "assistant", "content": response.content})

        # ツールがコンテナ内で実行された場合、ファイルを保持するためにそのIDを送り返します
        if response.container:
            container_id = response.container.id

    # max_requests回のリクエスト後も一時停止している場合:最後のレスポンスを返します
    return response


response = handle_server_tool_conversation(
    client,
    "Use the code execution tool to find the 1,000th prime number.",
    [{"type": "code_execution_20250825", "name": "code_execution"}],
)
print(response.stop_reason)

この関数は最後のレスポンスを返しますが、そこにはターンの最後の部分しか含まれていません。ターン全体を表示したり会話を続行したりするには、一時停止したレスポンスも保持してください。

停止理由とエラーの違い

stop_reason の値と実際のエラーを区別することが重要です:

停止理由(成功したレスポンス)

  • レスポンスボディの一部
  • 生成が正常に停止した理由を示す
  • レスポンスには有効なコンテンツが含まれる

エラー(失敗したリクエスト)

  • HTTP ステータスコード 4xx または 5xx
  • リクエスト処理の失敗を示す
  • レスポンスにはエラーの詳細が含まれる
client = anthropic.Anthropic()

try:
    response = client.messages.create(
        model="claude-opus-5-5",
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello!"}],
    )

    # stop_reason を含む成功レスポンスを処理します
    if response.stop_reason == "max_tokens":
        print("Response was truncated")

except anthropic.APIStatusError as e:
    # 実際のエラーを処理します
    match e.status_code:
        case 429:
            print("Rate limit exceeded")
        case 500:
            print("Server error")

ストリーミングに関する考慮事項

ストリーミングを使用する場合、stop_reason は次のようになります:

  • 最初の message_start イベントでは null
  • message_delta イベントで提供される
  • その他のイベントでは提供されない
client = anthropic.Anthropic()

with client.messages.stream(
    model="claude-opus-5-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
) as stream:
    for event in stream:
        if event.type == "message_delta":
            stop_reason = event.delta.stop_reason
            if stop_reason:
                print(f"Stream ended with: {stop_reason}")

一般的なパターン

ツール使用ワークフローの処理

def complete_tool_workflow(client, user_query, tools):
    messages = [{"role": "user", "content": user_query}]

    while True:
        response = client.messages.create(
            model="claude-opus-5-5",
            max_tokens=1024,
            messages=messages,
            tools=tools,
        )

        if response.stop_reason == "tool_use":
            # ツールを実行して続行します
            tool_results = execute_tools(response.content)
            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})
        else:
            # 最終レスポンス
            return response

完全なレスポンスを確保する

def get_complete_response(client, prompt, max_attempts=3):
    messages = [{"role": "user", "content": prompt}]
    full_response = ""

    for _ in range(max_attempts):
        response = client.messages.create(
            model="claude-opus-5-5", messages=messages, max_tokens=4096
        )

        full_response += next(
            (block.text for block in response.content if block.type == "text"), ""
        )

        if response.stop_reason != "max_tokens":
            break

        # 中断したところから再開します
        messages = [
            {"role": "user", "content": prompt},
            {"role": "assistant", "content": full_response},
            {"role": "user", "content": "Please continue from where you left off."},
        ]

    return full_response

入力サイズを知らずに最大トークン数を取得する

model_context_window_exceeded の停止理由を使用すると、入力サイズを計算せずに可能な最大トークン数をリクエストできます:

def get_max_possible_tokens(client, prompt):
    """
    Get as many tokens as possible within the model's context window
    without needing to calculate input token count
    """
    response = client.beta.messages.create(
        model="claude-opus-5-5",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=20000,  # Python SDK requires streaming for max_tokens above ~21k
    )

    match response.stop_reason:
        case "model_context_window_exceeded":
            # 入力サイズに対して可能な最大トークン数を取得しました
            print(
                f"Generated {response.usage.output_tokens} tokens (context limit reached)"
            )
        case "max_tokens":
            # 要求どおりのトークン数を取得しました
            print(
                f"Generated {response.usage.output_tokens} tokens (max_tokens reached)"
            )
        case _:
            # 自然な完了です
            print(
                f"Generated {response.usage.output_tokens} tokens (natural completion)"
            )

    return next((block.text for block in response.content if block.type == "text"), "")

次のステップ

拒否されたリクエストを、サーバー側またはクライアントでフォールバックモデルを使ってリトライします。

tool_use ループ、結果のフォーマット、リトライを SDK に任せます。

ストリーミング時に message_delta イベントから stop_reason を読み取ります。

停止理由とは異なる 4xx および 5xx の HTTP エラーを処理します。

このページは役に立ちましたか?