Was this page helpful?
Messages API にリクエストを送信すると、Claude のレスポンスには stop_reason フィールドが含まれており、モデルがレスポンス生成を停止した理由を示します。これらの値を理解することは、異なるレスポンスタイプを適切に処理する堅牢なアプリケーションを構築するために重要です。
API レスポンスの stop_reason の詳細については、Messages API リファレンスを参照してください。
stop_reason フィールドは、すべての成功した Messages API レスポンスに含まれます。リクエスト処理の失敗を示すエラーとは異なり、stop_reason は Claude がレスポンス生成を正常に完了した理由を示します。
{
"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,
"usage": {
"input_tokens": 100,
"output_tokens": 50
}
}最も一般的な停止理由です。Claude がレスポンスを自然に完了したことを示します。
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
if response.stop_reason == "end_turn":
# Process the complete response
print(response.content[0].text)時々、Claude は空のレスポンス(コンテンツのない正確に 2~3 トークン)を stop_reason: "end_turn" で返します。これは通常、Claude がアシスタントのターンが完了したと解釈する場合、特にツール結果の後に発生します。
一般的な原因:
空のレスポンスを防ぐ方法:
# INCORRECT: Adding text immediately after tool_result
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 5678"},
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_123",
"name": "calculator",
"input": {"operation": "add", "a": 1234, "b": 5678},
}
],
},
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_123", "content": "6912"},
{
"type": "text",
"text": "Here's the result", # Don't add text after tool_result
},
],
},
]
# CORRECT: Send tool results directly without additional text
messages = [
{"role": "user", "content": "Calculate the sum of 1234 and 5678"},
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_123",
"name": "calculator",
"input": {"operation": "add", "a": 1234, "b": 5678},
}
],
},
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_123", "content": "6912"}
],
}, # Just the tool_result, no additional text
]
# If you still get empty responses after fixing the above:
def handle_empty_response(client, messages):
response = client.messages.create(
model="claude-opus-4-7", max_tokens=1024, messages=messages
)
# Check if response is empty
if response.stop_reason == "end_turn" and not response.content:
# INCORRECT: Don't just retry with the empty response
# This won't work because Claude already decided it's done
# CORRECT: Add a continuation prompt in a NEW user message
messages.append({"role": "user", "content": "Please continue"})
response = client.messages.create(
model="claude-opus-4-7", max_tokens=1024, messages=messages
)
return responseベストプラクティス:
Claude がリクエストで指定された max_tokens 制限に達したため停止しました。
# Request with limited tokens
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=10,
messages=[{"role": "user", "content": "Explain quantum physics"}],
)
if response.stop_reason == "max_tokens":
# Response was truncated
print("Response was cut off at token limit")
# Consider making another request to continueClaude のレスポンスが max_tokens 制限に達したため切り詰められ、切り詰められたレスポンスに不完全なツール使用ブロックが含まれている場合、より高い max_tokens 値でリクエストを再試行して、完全なツール使用を取得する必要があります。
Claude がカスタム停止シーケンスの 1 つに遭遇しました。
response = client.messages.create(
model="claude-opus-4-7",
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}")Claude がツールを呼び出しており、実行することを期待しています。
ほとんどのツール使用実装では、ツール実行、結果のフォーマット、および会話管理を自動的に処理するツールランナーを使用することをお勧めします。
from anthropic import Anthropic
client = 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):
"""Execute a tool and return the result."""
return f"Weather in {tool_input.get('location', 'unknown')}: 72°F"
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=[weather_tool],
messages=[{"role": "user", "content": "What's the weather?"}],
)
if response.stop_reason == "tool_use":
# Extract and execute the tool
for content in response.content:
if content.type == "tool_use":
result = execute_tool(content.name, content.input)
# Return result to Claude for final responseWeb 検索や Web フェッチなどのサーバーツールを実行中にサーバー側のサンプリングループが反復制限に達したときに返されます。デフォルトの制限はリクエストあたり 10 回の反復です。
これが発生すると、レスポンスに対応する server_tool_result のない server_tool_use ブロックが含まれる場合があります。Claude が処理を完了できるようにするには、レスポンスをそのまま送り返して会話を続けます。
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
messages=[{"role": "user", "content": "Search for latest AI news"}],
)
if response.stop_reason == "pause_turn":
# Continue the conversation by sending the response back
messages = [
{"role": "user", "content": original_query},
{"role": "assistant", "content": response.content},
]
continuation = client.messages.create(
model="claude-opus-4-7",
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search"}],
)サーバーツールを使用するエージェントループで pause_turn を処理する必要があります。アシスタントのレスポンスをメッセージ配列に追加して、別の API リクエストを実行して Claude に続行させるだけです。
Claude は安全上の懸念からレスポンス生成を拒否しました。
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
messages=[{"role": "user", "content": "[Unsafe request]"}],
)
if response.stop_reason == "refusal":
# Claude declined to respond
print("Claude was unable to process this request")
# Consider rephrasing or modifying the requestClaude Sonnet 4.5 または Opus 4.1 を使用中に refusal 停止理由が頻繁に発生する場合は、API 呼び出しを Haiku 4.5(claude-haiku-4-5-20251001)を使用するように更新してみてください。これは異なる使用制限があります。Sonnet 4.5 の API 安全フィルターの理解について詳しく学びます。
Claude Sonnet 4.5 の API 安全フィルターによってトリガーされた拒否について詳しく知るには、Sonnet 4.5 の API 安全フィルターの理解を参照してください。
Claude がモデルのコンテキストウィンドウ制限に達したため停止しました。これにより、正確な入力サイズを知らなくても最大限のトークンをリクエストできます。
# Request with maximum tokens to get as much as possible
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=64000, # Practical non-streaming ceiling (Opus 4.7 supports 128K with streaming)
messages=[
{"role": "user", "content": "Large input that uses most of context window..."}
],
)
if response.stop_reason == "model_context_window_exceeded":
# Response hit context window limit before max_tokens
print("Response reached model's context window limit")
# The response is still valid but was limited by context windowこの停止理由は Sonnet 4.5 以降のモデルではデフォルトで利用可能です。以前のモデルの場合は、ベータヘッダー model-context-window-exceeded-2025-08-26 を使用してこの動作を有効にします。
レスポンス処理ロジックで stop_reason をチェックする習慣をつけます:
def handle_response(response):
if response.stop_reason == "tool_use":
return handle_tool_use(response)
elif response.stop_reason == "max_tokens":
return handle_truncation(response)
elif response.stop_reason == "model_context_window_exceeded":
return handle_context_limit(response)
elif response.stop_reason == "pause_turn":
return handle_pause(response)
elif response.stop_reason == "refusal":
return handle_refusal(response)
else:
# Handle end_turn and other cases
return response.content[0].textトークン制限またはコンテキストウィンドウによってレスポンスが切り詰められた場合:
def handle_truncated_response(response):
if response.stop_reason in ["max_tokens", "model_context_window_exceeded"]:
# Option 1: Warn the user about the specific limit
if response.stop_reason == "max_tokens":
message = "[Response truncated due to max_tokens limit]"
else:
message = "[Response truncated due to context window limit]"
return f"{response.content[0].text}\n\n{message}"
# Option 2: Continue generation
messages = [
{"role": "user", "content": original_prompt},
{"role": "assistant", "content": response.content[0].text},
]
continuation = client.messages.create(
model="claude-opus-4-7",
max_tokens=1024,
messages=messages + [{"role": "user", "content": "Please continue"}],
)
return response.content[0].text + continuation.content[0].textサーバーツールを使用する場合、サーバー側のサンプリングループが反復制限に達すると API が pause_turn を返す場合があります(デフォルト 10)。会話を続けることでこれを処理します:
def handle_server_tool_conversation(client, user_query, tools, max_continuations=5):
"""
Handle server tool conversations that may require multiple continuations.
The server runs a sampling loop when executing server tools. If the loop
reaches its iteration limit, the API returns pause_turn. Continue the
conversation by sending the response back to let Claude finish.
"""
messages = [{"role": "user", "content": user_query}]
for _ in range(max_continuations):
response = client.messages.create(
model="claude-opus-4-7", messages=messages, tools=tools
)
if response.stop_reason != "pause_turn":
# Claude finished processing - return the final response
return response
# pause_turn: replace the full message list to maintain alternating roles
messages = [
{"role": "user", "content": user_query},
{"role": "assistant", "content": response.content},
]
# Reached max continuations - return the last response
return responsestop_reason 値と実際のエラーを区別することが重要です:
import anthropic
from anthropic import Anthropic
client = Anthropic()
try:
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
# Handle successful response with stop_reason
if response.stop_reason == "max_tokens":
print("Response was truncated")
except anthropic.APIError as e:
# Handle actual errors
if e.status_code == 429:
print("Rate limit exceeded")
elif e.status_code == 500:
print("Server error")ストリーミングを使用する場合、stop_reason は:
message_start イベントでは nullmessage_delta イベントで提供されるfrom anthropic import Anthropic
client = Anthropic()
with client.messages.stream(
model="claude-sonnet-4-20250514",
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-4-7", messages=messages, tools=tools
)
if response.stop_reason == "tool_use":
# Execute tools and continue
tool_results = execute_tools(response.content)
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
else:
# Final response
return responsedef 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-4-7", messages=messages, max_tokens=4096
)
full_response += response.content[0].text
if response.stop_reason != "max_tokens":
break
# Continue from where it left off
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": full_response},
{"role": "user", "content": "Please continue from where you left off."},
]
return full_responsemodel_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.messages.create(
model="claude-opus-4-7",
messages=[{"role": "user", "content": prompt}],
max_tokens=64000, # Practical non-streaming ceiling (Opus 4.7 supports 128K with streaming)
)
if response.stop_reason == "model_context_window_exceeded":
# Got the maximum possible tokens given input size
print(
f"Generated {response.usage.output_tokens} tokens (context limit reached)"
)
elif response.stop_reason == "max_tokens":
# Got exactly the requested tokens
print(f"Generated {response.usage.output_tokens} tokens (max_tokens reached)")
else:
# Natural completion
print(f"Generated {response.usage.output_tokens} tokens (natural completion)")
return response.content[0].textstop_reason 値を適切に処理することで、異なるレスポンスシナリオを適切に処理し、より良いユーザー体験を提供する、より堅牢なアプリケーションを構築できます。
# Check if response was truncated during tool use
if response.stop_reason == "max_tokens":
# Check if the last content block is an incomplete tool_use
last_block = response.content[-1]
if last_block.type == "tool_use":
# Send the request with higher max_tokens
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=4096, # Increased limit
messages=messages,
tools=tools,
)