---
title: 중지 이유와 폴백
url: https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons
description: 각 stop_reason 값의 의미와 애플리케이션에서 잘림, 도구 사용, 일시 중지된 턴, 거부를 처리하는 방법을 알아보세요.
---

모든 Messages API 응답에는 Claude가 생성을 중지한 이유를 알려주는 [`stop_reason`](https://platform.claude.com/docs/ko/api/messages/create#message.stop_reason) 필드가 포함되어 있습니다. 이 필드를 확인하여 응답을 그대로 사용할지, 대화를 계속할지, 재시도할지, 아니면 다른 모델로 폴백할지 결정하세요.

전체 응답 스키마는 [Messages API 레퍼런스](https://platform.claude.com/docs/ko/api/messages/create)를 참조하세요.

## 빠른 참조

| 값                                                                                                                                            | 발생 시점                                  | 해야 할 일                                                                                                                                       |
| -------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| [`end_turn`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#end-turn)                                           | Claude가 자연스럽게 응답을 완료했습니다.              | 응답을 사용하세요.                                                                                                                                   |
| [`max_tokens`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#max-tokens)                                       | 응답이 `max_tokens` 한도에 도달했습니다.           | `max_tokens`를 늘리거나 [응답을 이어서 생성하세요](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#ensuring-complete-responses). |
| [`stop_sequence`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#stop-sequence)                                 | Claude가 `stop_sequences` 중 하나를 출력했습니다. | `stop_sequence`를 읽어 어느 것이 발동했는지 확인하세요.                                                                                                       |
| [`tool_use`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#tool-use)                                           | Claude가 도구를 호출하고 있습니다.                 | 도구를 실행하고 결과를 반환하세요. 결과 블록이 아직 없는 서버 도구 호출은 이후 응답에서 완료됩니다.                                                                                    |
| [`pause_turn`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#pause-turn)                                       | 서버 도구 루프가 반복 한도에 도달했습니다.               | 어시스턴트 콘텐츠를 다시 보내 계속하세요.                                                                                                                      |
| [`refusal`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#refusal)                                             | Claude가 응답을 거부했습니다.                    | `stop_details`를 읽고 [폴백 모델에서 재시도하세요](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback).                            |
| [`model_context_window_exceeded`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#model-context-window-exceeded) | 응답이 모델의 컨텍스트 윈도우를 가득 채웠습니다.            | 응답을 잘린 것으로 처리하세요.                                                                                                                            |

## stop\_reason 필드

`stop_reason` 필드는 모든 성공적인 Messages API 응답의 일부입니다. 요청 처리 실패를 나타내는 오류와 달리, `stop_reason`은 Claude가 응답 생성을 완료한 이유를 알려줍니다.

```json 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가 자연스럽게 응답을 완료했음을 나타냅니다.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello!"}]
    }'
  ```

  ```bash CLI
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello!"}'
  ```

  ```python Python
  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)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

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

  if (response.stop_reason === "end_turn") {
    // 전체 응답을 처리합니다
    const textBlock = response.content.find((block) => block.type === "text");
    console.log(textBlock?.text);
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new UserMessageParam { Content = "Hello!" }]
  });

  if (response.StopReason == "end_turn")
  {
      // 전체 응답을 처리합니다
      foreach (var block in response.Content)
      {
          if (block.TryPickText(out var textBlock))
          {
              Console.WriteLine(textBlock.Text);
          }
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello!")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "end_turn" {
  	// 전체 응답을 처리합니다
  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  			fmt.Println(textBlock.Text)
  		}
  	}
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024L)
          .addUserMessage("Hello!")
          .build()
  );

  if (response.stopReason().map(StopReason.END_TURN::equals).orElse(false)) {
      // 전체 응답을 처리합니다
      response.content().stream()
          .flatMap(block -> block.text().stream())
          .forEach(textBlock -> IO.println(textBlock.text()));
  }
  ```

  ```php PHP
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello!']],
      model: 'claude-opus-5-5',
  );

  if ($response->stopReason === 'end_turn') {
      // 전체 응답을 처리합니다
      foreach ($response->content as $block) {
          if ($block->type === 'text') {
              echo $block->text, PHP_EOL;
          }
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }]
  )

  if response.stop_reason == :end_turn
    # 전체 응답을 처리합니다
    response.content.each do |block|
      puts block.text if block.type == :text
    end
  end
  ```
</CodeGroup>

<Accordion title="end_turn과 함께 반환되는 빈 응답">
  때때로 Claude는 `stop_reason: "end_turn"`과 함께 빈 응답(콘텐츠 없이 정확히 2\~3개의 토큰)을 반환합니다. 이는 일반적으로 Claude가 어시스턴트 턴이 완료되었다고 판단할 때, 특히 도구 결과 이후에 발생합니다.

  **일반적인 원인:**

  * 도구 결과 바로 뒤에 텍스트 블록 추가(이 경우 기록에 모든 도구 결과 뒤에 사용자 텍스트가 나타나므로, Claude는 그 패턴을 따라 턴을 종료합니다)
  * Claude의 완료된 응답을 아무것도 추가하지 않고 다시 보내는 경우 (Claude는 이미 완료되었다고 판단했으므로 계속 완료 상태로 남습니다)

  **빈 응답을 방지하는 방법:**

  <CodeGroup exclude="shell">
    ```python Python
    # 잘못된 예: 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
                },
            ],
        },
    ]

    # 올바른 예: 추가 텍스트 없이 도구 결과를 직접 전송
    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"}
            ],
        },  # Only the tool_result, no additional text
    ]
    ```

    ```typescript TypeScript
    // 잘못된 예: tool_result 바로 뒤에 텍스트 추가
    let messages: Anthropic.MessageParam[] = [
      { 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
        ]
      }
    ];

    // 올바른 예: 추가 텍스트 없이 도구 결과를 직접 전송
    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",
        // 추가 텍스트 없이 tool_result만 포함
        content: [{ type: "tool_result", tool_use_id: "toolu_123", content: "6912" }]
      }
    ];
    ```

    ```csharp C#
    using System.Text.Json;
    using Anthropic.Models.Messages;

    var input = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(
        """{"operation":"add","a":1234,"b":5678}"""
    )!;

    // 잘못된 예: tool_result 바로 뒤에 텍스트 추가
    List<MessageParam> messages =
    [
        new UserMessageParam { Content = "Calculate the sum of 1234 and 5678" },
        new AssistantMessageParam
        {
            Content = new List<ContentBlockParam>
            {
                new ToolUseBlockParam { ID = "toolu_123", Name = "calculator", Input = input }
            }
        },
        new UserMessageParam
        {
            Content = new List<ContentBlockParam>
            {
                new ToolResultBlockParam { ToolUseID = "toolu_123", Content = "6912" },
                new TextBlockParam { Text = "Here's the result" } // Don't add text after tool_result
            }
        }
    ];

    // 올바른 예: 추가 텍스트 없이 도구 결과를 직접 전송
    messages =
    [
        new UserMessageParam { Content = "Calculate the sum of 1234 and 5678" },
        new AssistantMessageParam
        {
            Content = new List<ContentBlockParam>
            {
                new ToolUseBlockParam { ID = "toolu_123", Name = "calculator", Input = input }
            }
        },
        new UserMessageParam
        {
            // tool_result만 포함하고 추가 텍스트는 없음
            Content = new List<ContentBlockParam>
            {
                new ToolResultBlockParam { ToolUseID = "toolu_123", Content = "6912" }
            }
        }
    ];
    ```

    ```go Go
    input := map[string]any{"operation": "add", "a": 1234, "b": 5678}

    // 잘못된 예: tool_result 바로 뒤에 텍스트 추가
    messages := []anthropic.MessageParam{
    	anthropic.NewUserMessage(anthropic.NewTextBlock("Calculate the sum of 1234 and 5678")),
    	anthropic.NewAssistantMessage(
    		anthropic.NewToolUseBlock("toolu_123", input, "calculator"),
    	),
    	anthropic.NewUserMessage(
    		anthropic.NewToolResultBlock("toolu_123", "6912", false),
    		anthropic.NewTextBlock("Here's the result"), // Don't add text after tool_result
    	),
    }

    // 올바른 예: 추가 텍스트 없이 도구 결과를 직접 전송
    messages = []anthropic.MessageParam{
    	anthropic.NewUserMessage(anthropic.NewTextBlock("Calculate the sum of 1234 and 5678")),
    	anthropic.NewAssistantMessage(
    		anthropic.NewToolUseBlock("toolu_123", input, "calculator"),
    	),
    	// tool_result만 포함하고 추가 텍스트는 없음
    	anthropic.NewUserMessage(
    		anthropic.NewToolResultBlock("toolu_123", "6912", false),
    	),
    }
    ```

    ```java Java
    ToolUseBlockParam toolUse = ToolUseBlockParam.builder()
        .id("toolu_123")
        .name("calculator")
        .input(ToolUseBlockParam.Input.builder()
            .putAdditionalProperty("operation", JsonValue.from("add"))
            .putAdditionalProperty("a", JsonValue.from(1234))
            .putAdditionalProperty("b", JsonValue.from(5678))
            .build())
        .build();

    // 잘못된 예: tool_result 바로 뒤에 텍스트를 추가함
    List<MessageParam> messages = List.of(
        MessageParam.ofUser("Calculate the sum of 1234 and 5678"),
        MessageParam.ofAssistantOfContentBlockParams(List.of(ContentBlockParam.ofToolUse(toolUse))),
        MessageParam.ofUserOfContentBlockParams(List.of(
            ContentBlockParam.ofToolResult(
                ToolResultBlockParam.builder()
                    .toolUseId("toolu_123")
                    .content("6912")
                    .build()),
            // tool_result 뒤에 텍스트를 추가하지 마세요
            ContentBlockParam.ofText(TextBlockParam.builder()
                .text("Here's the result")
                .build())
        ))
    );

    // 올바른 예: 추가 텍스트 없이 도구 결과를 직접 전송함
    messages = List.of(
        MessageParam.ofUser("Calculate the sum of 1234 and 5678"),
        MessageParam.ofAssistantOfContentBlockParams(List.of(ContentBlockParam.ofToolUse(toolUse))),
        // 추가 텍스트 없이 tool_result만 포함
        MessageParam.ofUserOfContentBlockParams(List.of(
            ContentBlockParam.ofToolResult(
                ToolResultBlockParam.builder()
                    .toolUseId("toolu_123")
                    .content("6912")
                    .build())
        ))
    );
    ```

    ```php PHP
    // 잘못된 예: 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'],
                // tool_result 뒤에 텍스트를 추가하지 마세요
                ['type' => 'text', 'text' => "Here's the 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',
            // tool_result만 포함하고 추가 텍스트는 넣지 않음
            'content' => [
                ['type' => 'tool_result', 'tool_use_id' => 'toolu_123', 'content' => '6912'],
            ],
        ],
    ];
    ```

    ```ruby Ruby
    # 잘못된 예: 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" },
          # tool_result 뒤에 텍스트를 추가하지 마세요
          { type: "text", text: "Here's the 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",
        # 추가 텍스트 없이 tool_result만 포함
        content: [
          { type: "tool_result", tool_use_id: "toolu_123", content: "6912" }
        ]
      }
    ]
    ```
  </CodeGroup>

  메시지 구조를 수정한 후에도 여전히 빈 응답을 받는다면, 빈 응답으로 재시도하는 대신 새 사용자 메시지에 계속 진행 프롬프트를 추가하세요:

  <CodeGroup exclude="shell">
    ```python Python
    def handle_empty_response(client, messages):
        response = client.messages.create(
            model="claude-opus-5-5", max_tokens=1024, messages=messages
        )

        # 응답이 비어 있는지 확인합니다
        if response.stop_reason == "end_turn" and not response.content:
            # 잘못된 방법: 빈 응답을 그대로 두고 재시도하지 마세요
            # Claude가 이미 완료했다고 판단했으므로 이 방법은 작동하지 않습니다

            # 올바른 방법: 새 user 메시지에 계속 진행하라는 프롬프트를 추가하세요
            messages.append({"role": "user", "content": "Please continue"})

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

        return response
    ```

    ```typescript TypeScript
    async function handleEmptyResponse(
      client: Anthropic,
      messages: Anthropic.MessageParam[]
    ): Promise<Anthropic.Message> {
      let response = await client.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        messages
      });

      // 응답이 비어 있는지 확인합니다
      if (response.stop_reason === "end_turn" && response.content.length === 0) {
        // 잘못된 예: 빈 응답을 그대로 두고 재시도하지 마세요
        // Claude가 이미 완료했다고 판단했으므로 이 방법은 작동하지 않습니다

        // 올바른 예: 새 사용자 메시지에 계속 진행하라는 프롬프트를 추가하세요
        messages.push({ role: "user", content: "Please continue" });

        response = await client.messages.create({
          model: "claude-opus-5-5",
          max_tokens: 1024,
          messages
        });
      }

      return response;
    }
    ```

    ```csharp C#
    static async Task<Message> HandleEmptyResponse(AnthropicClient client, List<MessageParam> messages)
    {
        var response = await client.Messages.Create(new MessageCreateParams
        {
            Model = Model.ClaudeOpus5_5,
            MaxTokens = 1024,
            Messages = messages
        });

        // 응답이 비어 있는지 확인합니다
        if (response.StopReason == "end_turn" && response.Content.Count == 0)
        {
            // 올바른 방법: 새 사용자 메시지에 이어서 작성하도록 요청하는 프롬프트를 추가합니다
            messages.Add(new UserMessageParam { Content = "Please continue" });

            response = await client.Messages.Create(new MessageCreateParams
            {
                Model = Model.ClaudeOpus5_5,
                MaxTokens = 1024,
                Messages = messages
            });
        }

        return response;
    }
    ```

    ```go Go
    func handleEmptyResponse(client anthropic.Client, messages []anthropic.MessageParam) (*anthropic.Message, error) {
    	response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    		Model:     anthropic.ModelClaudeOpus5_5,
    		MaxTokens: 1024,
    		Messages:  messages,
    	})
    	if err != nil {
    		return nil, err
    	}

    	// 응답이 비어 있는지 확인합니다
    	if response.StopReason == "end_turn" && len(response.Content) == 0 {
    		// 올바른 방법: 새 user 메시지에 계속 진행을 요청하는 프롬프트를 추가합니다
    		messages = append(messages, anthropic.NewUserMessage(anthropic.NewTextBlock("Please continue")))

    		response, err = client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    			Model:     anthropic.ModelClaudeOpus5_5,
    			MaxTokens: 1024,
    			Messages:  messages,
    		})
    		if err != nil {
    			return nil, err
    		}
    	}

    	return response, nil
    }
    ```

    ```java Java
    static Message handleEmptyResponse(AnthropicClient client, List<MessageParam> messages) {
        Message response = client.messages().create(
            MessageCreateParams.builder()
                .model(Model.CLAUDE_OPUS_5_5)
                .maxTokens(1024L)
                .messages(messages)
                .build()
        );

        // 응답이 비어 있는지 확인합니다
        boolean isEndTurn = response.stopReason().map(StopReason.END_TURN::equals).orElse(false);
        if (isEndTurn && response.content().isEmpty()) {
            // 올바른 방법: 새 user 메시지에 계속 진행 프롬프트를 추가합니다
            List<MessageParam> extended = new ArrayList<>(messages);
            extended.add(MessageParam.ofUser("Please continue"));

            response = client.messages().create(
                MessageCreateParams.builder()
                    .model(Model.CLAUDE_OPUS_5_5)
                    .maxTokens(1024L)
                    .messages(extended)
                    .build()
            );
        }

        return response;
    }
    ```

    ```php PHP
    function handle_empty_response(Client $client, array $messages)
    {
        $response = $client->messages->create(
            maxTokens: 1024,
            messages: $messages,
            model: 'claude-opus-5-5',
        );

        // 응답이 비어 있는지 확인합니다
        if ($response->stopReason === 'end_turn' && count($response->content) === 0) {
            // 올바른 방법: 새 user 메시지에 계속 진행 프롬프트를 추가합니다
            $messages[] = ['role' => 'user', 'content' => 'Please continue'];

            $response = $client->messages->create(
                maxTokens: 1024,
                messages: $messages,
                model: 'claude-opus-5-5',
            );
        }

        return $response;
    }
    ```

    ```ruby Ruby
    def handle_empty_response(client, messages)
      response = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 1024,
        messages: messages
      )

      # 응답이 비어 있는지 확인합니다
      if response.stop_reason == :end_turn && response.content.empty?
        # 올바른 방법: 새 user 메시지에 계속 진행 프롬프트를 추가합니다
        messages << { role: "user", content: "Please continue" }

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

      response
    end
    ```
  </CodeGroup>

  **모범 사례:**

  1. **도구 결과 바로 뒤에 텍스트 블록을 절대 추가하지 마세요:** 이는 모든 도구 사용 후에 사용자 입력이 오는 패턴을 만들며, Claude는 턴을 종료하는 방식으로 그 패턴을 따릅니다.
  2. **빈 응답을 수정 없이 재시도하지 마세요:** 빈 응답을 다시 보내는 것은 도움이 되지 않습니다.
  3. **계속 진행 프롬프트는 최후의 수단으로 사용하세요:** 위의 수정 사항으로 문제가 해결되지 않는 경우에만 사용하세요.
</Accordion>

### max\_tokens

Claude가 요청에 지정된 [`max_tokens`](https://platform.claude.com/docs/ko/api/messages/create#create.max_tokens) 한도에 도달하여 중지했습니다.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 10,
      "messages": [{"role": "user", "content": "Explain quantum physics"}]
    }'
  ```

  ```bash CLI
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 10 \
    --message '{role: user, content: "Explain quantum physics"}'
  ```

  ```python Python
  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")
      # 이어서 받으려면 추가 요청을 보내는 것을 고려하세요
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  // 제한된 토큰으로 요청합니다
  const response = await client.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 10,
    messages: [{ role: "user", content: "Explain quantum physics" }]
  });

  if (response.stop_reason === "max_tokens") {
    // 응답이 잘렸습니다
    console.log("Response was cut off at token limit");
    // 계속하려면 추가 요청을 보내는 것을 고려하세요
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  // 제한된 토큰으로 요청합니다
  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 10,
      Messages = [new UserMessageParam { Content = "Explain quantum physics" }]
  });

  if (response.StopReason == "max_tokens")
  {
      // 응답이 잘렸습니다
      Console.WriteLine("Response was cut off at token limit");
      // 이어서 받으려면 추가 요청을 보내는 것을 고려하세요
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  // 제한된 토큰으로 요청합니다
  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 10,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Explain quantum physics")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "max_tokens" {
  	// 응답이 잘렸습니다
  	fmt.Println("Response was cut off at token limit")
  	// 이어서 생성하려면 추가 요청을 보내는 것을 고려하세요
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  // 제한된 토큰으로 요청합니다
  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(10L)
          .addUserMessage("Explain quantum physics")
          .build()
  );

  if (response.stopReason().map(StopReason.MAX_TOKENS::equals).orElse(false)) {
      // 응답이 잘렸습니다
      IO.println("Response was cut off at token limit");
      // 계속하려면 추가 요청을 보내는 것을 고려하세요
  }
  ```

  ```php PHP
  $client = new Client();

  // 제한된 토큰으로 요청합니다
  $response = $client->messages->create(
      maxTokens: 10,
      messages: [['role' => 'user', 'content' => 'Explain quantum physics']],
      model: Model::CLAUDE_OPUS_5_5,
  );

  if ($response->stopReason === 'max_tokens') {
      // 응답이 잘렸습니다
      echo 'Response was cut off at token limit', PHP_EOL;
      // 이어서 생성하려면 추가 요청을 보내는 것을 고려하세요
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  # 제한된 토큰으로 요청합니다
  response = client.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 10,
    messages: [{ role: "user", content: "Explain quantum physics" }]
  )

  if response.stop_reason == :max_tokens
    # 응답이 잘렸습니다
    puts "Response was cut off at token limit"
    # 계속하려면 추가 요청을 보내는 것을 고려하세요
  end
  ```
</CodeGroup>

<Accordion title="불완전한 도구 사용 블록">
  Claude의 응답이 `max_tokens` 한도에 도달하여 잘렸고, 잘린 응답에 불완전한 도구 사용 블록이 포함되어 있다면, 전체 도구 사용을 얻기 위해 더 높은 `max_tokens` 값으로 요청을 재시도해야 합니다.

  <CodeGroup exclude="shell:cURL, shell:CLI">
    <CodeGroupItem language="cURL">
      이 워크플로에는 단일 셸 명령보다 더 많은 로직이 필요합니다. 실행하려면 [Claude SDK](https://platform.claude.com/docs/ko/cli-sdks-libraries/overview)를 사용하세요.
    </CodeGroupItem>

    <CodeGroupItem language="CLI">
      이 워크플로에는 단일 셸 명령보다 더 많은 로직이 필요합니다. 실행하려면 [Claude SDK](https://platform.claude.com/docs/ko/cli-sdks-libraries/overview)를 사용하세요.
    </CodeGroupItem>

    ```python Python
    # 도구 사용 중 응답이 잘렸는지 확인합니다
    if response.stop_reason == "max_tokens":
        # 마지막 콘텐츠 블록이 불완전한 tool_use인지 확인합니다
        last_block = response.content[-1]
        if last_block.type == "tool_use":
            # 더 높은 max_tokens 값으로 요청을 보냅니다
            response = client.messages.create(
                model="claude-opus-5-5",
                max_tokens=4096,  # Increased limit
                messages=messages,
                tools=tools,
            )
    ```

    ```typescript TypeScript
    // 도구 사용 중 응답이 잘렸는지 확인합니다
    if (response.stop_reason === "max_tokens") {
      // 마지막 콘텐츠 블록이 불완전한 tool_use인지 확인합니다
      const lastBlock = response.content[response.content.length - 1];
      if (lastBlock.type === "tool_use") {
        // 더 높은 max_tokens 값으로 요청을 보냅니다
        response = await client.messages.create({
          model: "claude-opus-5-5",
          max_tokens: 4096, // Increased limit
          messages: messages,
          tools: tools
        });
      }
    }
    ```

    ```csharp C#
    using System.Linq;
    using Anthropic;
    using Anthropic.Models.Messages;

    AnthropicClient client = new();

    var parameters = new MessageCreateParams
    {
        Model = Model.ClaudeOpus5_5,
        MaxTokens = 1024,
        Messages = messages,
        Tools = tools
    };

    var response = await client.Messages.Create(parameters);

    if (response.StopReason == "max_tokens")
    {
        var lastBlock = response.Content.Last();
        if (lastBlock.TryPickToolUse(out _))
        {
            response = await client.Messages.Create(parameters with { MaxTokens = 4096 });
        }
    }
    ```

    ```go Go
    response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    	Model:     anthropic.ModelClaudeOpus5_5,
    	MaxTokens: 1024,
    	Messages:  messages,
    	Tools:     tools,
    })
    if err != nil {
    	log.Fatal(err)
    }

    if response.StopReason == "max_tokens" {
    	lastBlock := response.Content[len(response.Content)-1]
    	switch lastBlock.AsAny().(type) {
    	case anthropic.ToolUseBlock:
    		response, err = client.Messages.New(context.TODO(), anthropic.MessageNewParams{
    			Model:     anthropic.ModelClaudeOpus5_5,
    			MaxTokens: 4096,
    			Messages:  messages,
    			Tools:     tools,
    		})
    		if err != nil {
    			log.Fatal(err)
    		}
    	}
    }
    ```

    ```java Java
    // 도구 사용 중에 응답이 잘렸는지 확인합니다
    if (response.stopReason().isPresent() && response.stopReason().get().equals(StopReason.MAX_TOKENS)) {
        ContentBlock lastBlock = response.content().get(response.content().size() - 1);
        if (lastBlock.toolUse().isPresent()) {
            // 더 높은 max_tokens 값으로 요청을 보냅니다
            response = client.messages().create(
                MessageCreateParams.builder()
                    .model(Model.CLAUDE_OPUS_5_5)
                    .maxTokens(4096L) // Increased limit
                    .messages(messages)
                    .tools(tools)
                    .build()
            );
        }
    }
    ```

    ```php PHP
    $response = $client->messages->create(
        maxTokens: 1024,
        messages: $messages,
        model: 'claude-opus-5-5',
        tools: $tools,
    );

    if ($response->stopReason === 'max_tokens') {
        $lastBlock = end($response->content);
        if ($lastBlock->type === 'tool_use') {
            $response = $client->messages->create(
                maxTokens: 4096,
                messages: $messages,
                model: 'claude-opus-5-5',
                tools: $tools,
            );
        }
    }
    ```

    ```ruby Ruby
    response = client.messages.create(
      model: Anthropic::Model::CLAUDE_OPUS_5_5,
      max_tokens: 1024,
      messages: messages,
      tools: tools
    )

    if response.stop_reason == :max_tokens
      last_block = response.content.last
      if last_block.type == :tool_use
        response = client.messages.create(
          model: Anthropic::Model::CLAUDE_OPUS_5_5,
          max_tokens: 4096,
          messages: messages,
          tools: tools
        )
      end
    end
    ```
  </CodeGroup>
</Accordion>

### stop\_sequence

Claude가 사용자 지정 [중지 시퀀스](https://platform.claude.com/docs/ko/api/messages/create#create.stop_sequences) 중 하나를 만났습니다.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "stop_sequences": ["END", "STOP"],
      "messages": [{"role": "user", "content": "Generate text until you say END"}]
    }'
  ```

  ```bash CLI
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --stop-sequence END --stop-sequence STOP \
    --message '{role: user, content: "Generate text until you say END"}'
  ```

  ```python Python
  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}")
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  const response = await 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") {
    console.log(`Stopped at sequence: ${response.stop_sequence}`);
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      StopSequences = ["END", "STOP"],
      Messages = [new UserMessageParam { Content = "Generate text until you say END" }]
  });

  if (response.StopReason == "stop_sequence")
  {
      Console.WriteLine($"Stopped at sequence: {response.StopSequence}");
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:         anthropic.ModelClaudeOpus5_5,
  	MaxTokens:     1024,
  	StopSequences: []string{"END", "STOP"},
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Generate text until you say END")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "stop_sequence" {
  	fmt.Printf("Stopped at sequence: %s\n", response.StopSequence)
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024L)
          .addStopSequence("END")
          .addStopSequence("STOP")
          .addUserMessage("Generate text until you say END")
          .build()
  );

  if (response.stopReason().map(StopReason.STOP_SEQUENCE::equals).orElse(false)) {
      IO.println("Stopped at sequence: " + response.stopSequence().orElse(""));
  }
  ```

  ```php PHP
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Generate text until you say END']],
      model: Model::CLAUDE_OPUS_5_5,
      stopSequences: ['END', 'STOP'],
  );

  if ($response->stopReason === 'stop_sequence') {
      echo "Stopped at sequence: {$response->stopSequence}", PHP_EOL;
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.messages.create(
    model: Anthropic::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
    puts "Stopped at sequence: #{response.stop_sequence}"
  end
  ```
</CodeGroup>

### tool\_use

Claude가 사용자가 실행해야 하는 도구를 호출하고 있습니다.

<Note>
  대부분의 도구 사용 구현에서는 도구 실행, 결과 포맷팅, 대화 관리를 자동으로 처리하는 [도구 러너](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/tool-runner)를 사용하세요.
</Note>

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "tools": [{
        "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"]
        }
      }],
      "messages": [{"role": "user", "content": "What is the weather in San Francisco?"}]
    }'
  ```

  ```bash CLI
  ant messages create <<'YAML'
  model: claude-opus-5-5
  max_tokens: 1024
  messages:
    - role: user
      content: What is the weather in San Francisco?
  tools:
    - 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]
  YAML
  ```

  ```python Python
  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에 반환합니다
  ```

  ```typescript TypeScript
  const client = new Anthropic();
  const weatherTool: Anthropic.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"]
    }
  };

  function executeTool(name: string, input: Record<string, string>): string {
    return `Weather in ${input.location ?? "unknown"}: 72°F`;
  }

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

  if (response.stop_reason === "tool_use") {
    // 도구를 추출하고 실행합니다
    for (const block of response.content) {
      if (block.type === "tool_use") {
        const result = executeTool(block.name, block.input as Record<string, string>);
        // 최종 응답을 위해 결과를 Claude에 반환합니다
      }
    }
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  var weatherTool = new Tool
  {
      Name = "get_weather",
      Description = "Get the current weather in a given location",
      InputSchema = new InputSchema
      {
          Properties = new Dictionary<string, JsonElement>
          {
              ["location"] = JsonSerializer.SerializeToElement(
                  new { type = "string", description = "City and state" }
              ),
          },
          Required = ["location"]
      }
  };

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Tools = [weatherTool],
      Messages = [new UserMessageParam { Content = "What is the weather in San Francisco?" }]
  });

  if (response.StopReason == "tool_use")
  {
      // 도구를 추출하여 실행합니다
      foreach (var block in response.Content)
      {
          if (block.TryPickToolUse(out var toolUse))
          {
              // toolUse.Input으로 toolUse.Name을 실행하고 결과를 Claude에 반환합니다
          }
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  weatherTool := anthropic.ToolParam{
  	Name:        "get_weather",
  	Description: anthropic.String("Get the current weather in a given location"),
  	InputSchema: anthropic.ToolInputSchemaParam{
  		Properties: map[string]any{
  			"location": map[string]string{"type": "string", "description": "City and state"},
  		},
  		Required: []string{"location"},
  	},
  }

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Tools:     []anthropic.ToolUnionParam{{OfTool: &weatherTool}},
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("What is the weather in San Francisco?")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "tool_use" {
  	// 도구를 추출하고 실행합니다
  	for _, block := range response.Content {
  		if toolUse, ok := block.AsAny().(anthropic.ToolUseBlock); ok {
  			fmt.Println(toolUse.Name, toolUse.Input)
  			// 최종 응답을 위해 결과를 Claude에 반환합니다
  		}
  	}
  }
  ```

  ```java Java
  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      Tool weatherTool = Tool.builder()
          .name("get_weather")
          .description("Get the current weather in a given location")
          .inputSchema(Tool.InputSchema.builder()
              .properties(JsonValue.from(Map.of(
                  "location", Map.of("type", "string", "description", "City and state")
              )))
              .putAdditionalProperty("required", JsonValue.from(List.of("location")))
              .build())
          .build();

      Message response = client.messages().create(
          MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .addTool(weatherTool)
              .addUserMessage("What is the weather in San Francisco?")
              .build()
      );

      if (response.stopReason().map(StopReason.TOOL_USE::equals).orElse(false)) {
          // 도구를 추출하고 실행합니다
          for (ContentBlock block : response.content()) {
              block.toolUse().ifPresent(toolUse -> {
                  // toolUse.input()으로 toolUse.name()을 실행하고 결과를 Claude에 반환합니다
              });
          }
      }
  ```

  ```php PHP
  $client = new Client();

  $weatherTool = [
      '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'],
      ],
  ];

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'What is the weather in San Francisco?']],
      model: 'claude-opus-5-5',
      tools: [$weatherTool],
  );

  if ($response->stopReason === 'tool_use') {
      // 도구를 추출하고 실행합니다
      foreach ($response->content as $block) {
          if ($block->type === 'tool_use') {
              // $block->input으로 $block->name을 실행하고 결과를 Claude에 반환합니다
          }
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  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"]
    }
  }

  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
    # 도구를 추출하여 실행합니다
    response.content.each do |block|
      next unless block.type == :tool_use
      # block.input으로 block.name을 실행하고 결과를 Claude에 반환합니다
    end
  end
  ```
</CodeGroup>

`tool_use` 응답에는 `id`와 일치하는 결과 블록이 없는 `server_tool_use` 블록이 포함될 수도 있습니다. 해당 서버 도구 호출은 완료되지 않았으며, 이 응답에는 그 결과가 포함되어 있지 않습니다. 일반적인 경우, Claude가 같은 병렬 도구 호출 그룹에서 [서버 도구](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/server-tools)와 클라이언트 도구 중 하나를 함께 호출하면, API는 사용자가 클라이언트 도구를 먼저 실행할 수 있도록 서버 도구를 실행하지 않고 반환합니다. 이 상태를 나타내는 다른 표시는 없으므로, 각 `server_tool_use` 또는 `mcp_tool_use` 블록의 `id`에 일치하는 결과 블록이 있는지 확인하여 감지하세요.

<Note>
  [프로그래매틱 도구 호출](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/programmatic-tool-calling)에서는 같은 응답 형태가 다른 의미를 갖습니다. 클라이언트 `tool_use` 블록은 Claude가 직접 생성한 것이 아니라 `code_execution` 도구에서 실행 중인 코드에서 나오며, 해당 블록의 `caller` 필드는 이를 호출한 `code_execution` 블록을 가리킵니다. 그 코드는 이미 시작되었습니다. 즉, `tool_result` 블록을 기다리며 일시 중지된 상태이고, 이를 보내면 지연된 도구를 시작하는 대신 실행이 재개됩니다. `code_execution` 블록 자체의 결과 블록은 코드가 완료되면 도착하며, 이는 두 번 이상의 도구 결과 라운드가 필요할 수 있습니다. 후속 사용자 메시지 자체는 두 경우 모두 동일합니다. 프로그래매틱 도구 호출의 경우, 해당 페이지에 나와 있는 것처럼 응답의 `container` 필드에 있는 `id`도 함께 전달하세요.
</Note>

```json 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` 블록마다 하나씩 `tool_result` 블록으로 구성된 사용자 메시지입니다([도구 호출 처리](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/handle-tool-calls) 참조). 여기에 두 가지 추가 규칙이 있습니다. 해당 메시지에는 `tool_result` 블록 외에 아무것도 포함되어서는 안 되며, 요청은 동일한 `tools` 배열을 유지해야 합니다. 대기 중인 서버 도구를 더 이상 정의하지 않는 재개 요청은 메시지가 ``but no `web_search` tool was provided``로 끝나는 400 오류로 실패합니다. API는 사용자의 결과를 아직 열려 있는 어시스턴트 턴에 첨부하고, 지연된 서버 도구를 실행하며(일시 중지된 코드 실행의 경우 재개하며), 턴을 계속합니다. Claude가 직접 호출한 서버 도구의 경우, 다음 응답의 `content`는 이전 응답의 `server_tool_use` `id`에 응답하는 결과 블록으로 시작합니다.

```json 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`로 실패합니다:

```text wrap
`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

웹 검색과 같은 [서버 도구](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/server-tools)를 실행하는 동안 서버 측 샘플링 루프가 반복 한도에 도달하면 반환됩니다. 기본 한도는 요청당 10회 반복입니다.

이 경우 응답에 대응하는 결과 블록이 없는 `server_tool_use` 블록이 포함될 수 있습니다. Claude가 처리를 마칠 수 있도록 응답을 그대로 다시 보내 대화를 계속하세요. 클라이언트 `tool_use` 블록이 사용자를 기다리는 상태로 남아 있는 응답은 `stop_reason`이 `pause_turn`이 되는 경우가 없습니다. Claude가 사용자의 도구를 호출하기 위해 중지하면 `stop_reason`은 [`tool_use`](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#tool-use)이며, 응답 자체 대신 클라이언트 `tool_result` 블록을 보내 계속 진행합니다.

<CodeGroup>
  ```bash cURL
  # SDK는 이어서 생성하기를 직접 처리합니다. cURL을 사용하는 경우 응답의 stop_reason을
  # 확인하고, assistant 콘텐츠를 추가하여 다시 POST 요청을 보내세요.
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "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"}]
    }'
  ```

  ```bash CLI
  # stop_reason 을 확인하세요. pause_turn 이면 assistant
  # 응답을 --message 에 추가하여 다시 실행하세요.
  ant messages create <<'YAML'
  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"}
  YAML
  ```

  ```python Python
  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"}],
      )
  ```

  ```typescript TypeScript
  const response = await 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") {
    // 응답을 다시 보내 대화를 이어 갑니다
    const continuation = await 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" },
        { role: "assistant", content: response.content }
      ]
    });
  }
  ```

  ```csharp C#
  List<ToolUnion> tools = [new ToolUnion(new WebSearchTool20250305())];
  MessageParam userMessage = new UserMessageParam { Content = "Search for latest AI news" };

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 4096,
      Tools = tools,
      Messages = [userMessage]
  });

  if (response.StopReason == "pause_turn")
  {
      // 응답을 다시 보내 대화를 이어 갑니다
      var continuation = await client.Messages.Create(new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 4096,
          Tools = tools,
          Messages =
          [
              userMessage,
              new AssistantMessageParam
              {
                  Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
              }
          ]
      });
  }
  ```

  ```go Go
  tools := []anthropic.ToolUnionParam{
  	{OfWebSearchTool20250305: &anthropic.WebSearchTool20250305Param{}},
  }
  userMessage := anthropic.NewUserMessage(anthropic.NewTextBlock("Search for latest AI news"))

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 4096,
  	Tools:     tools,
  	Messages:  []anthropic.MessageParam{userMessage},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "pause_turn" {
  	// 응답을 다시 보내 대화를 이어 갑니다
  	var contentParams []anthropic.ContentBlockParamUnion
  	for _, block := range response.Content {
  		contentParams = append(contentParams, block.ToParam())
  	}
  	continuation, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 4096,
  		Tools:     tools,
  		Messages:  []anthropic.MessageParam{userMessage, anthropic.NewAssistantMessage(contentParams...)},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	_ = continuation
  }
  ```

  ```java Java
  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(4096L)
          .addTool(WebSearchTool20250305.builder().build())
          .addUserMessage("Search for latest AI news")
          .build()
  );

  if (response.stopReason().map(StopReason.PAUSE_TURN::equals).orElse(false)) {
      // 응답을 다시 보내 대화를 이어 갑니다
      Message continuation = client.messages().create(
          MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(4096L)
              .addTool(WebSearchTool20250305.builder().build())
              .addUserMessage("Search for latest AI news")
              .addMessage(response)
              .build()
      );
  }
  ```

  ```php PHP
  $tools = [['type' => 'web_search_20250305', 'name' => 'web_search']];
  $userMessage = ['role' => 'user', 'content' => 'Search for latest AI news'];

  $response = $client->messages->create(
      maxTokens: 4096,
      messages: [$userMessage],
      model: 'claude-opus-5-5',
      tools: $tools,
  );

  if ($response->stopReason === 'pause_turn') {
      // 응답을 다시 전송하여 대화를 이어 갑니다
      $continuation = $client->messages->create(
          maxTokens: 4096,
          messages: [
              $userMessage,
              ['role' => 'assistant', 'content' => $response->content],
          ],
          model: 'claude-opus-5-5',
          tools: $tools,
      );
  }
  ```

  ```ruby Ruby
  tools = [{ type: "web_search_20250305", name: "web_search" }]
  user_message = { role: "user", content: "Search for latest AI news" }

  response = client.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 4096,
    tools: tools,
    messages: [user_message]
  )

  if response.stop_reason == :pause_turn
    # 응답을 다시 보내 대화를 이어 갑니다
    continuation = client.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 4096,
      tools: tools,
      messages: [user_message, { role: "assistant", content: response.content }]
    )
  end
  ```
</CodeGroup>

<Note>
  애플리케이션은 서버 도구를 사용하는 모든 에이전트 루프에서 `pause_turn`을 처리해야 합니다. 어시스턴트의 응답을 messages 배열에 추가하고 Claude가 계속할 수 있도록 또 다른 API 요청을 보내세요.
</Note>

### refusal

Claude가 응답 생성을 거부했습니다. 안전 분류기는 이 중지 이유를 오류가 아닌 정상적인 HTTP 200 응답으로 반환합니다.

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "[Unsafe request]"}]
    }'
  ```

  ```bash CLI
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "[Unsafe request]"}'
  ```

  ```python Python
  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")
      # 요청을 다른 표현으로 바꾸거나 수정해 보세요
  ```

  ```typescript TypeScript
  const client = new Anthropic();

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

  if (response.stop_reason === "refusal") {
    // Claude가 응답을 거부했습니다
    console.log("Claude was unable to process this request");
    // 요청을 다른 표현으로 바꾸거나 수정해 보세요
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  var response = await client.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new UserMessageParam { Content = "[Unsafe request]" }]
  });

  if (response.StopReason == "refusal")
  {
      // Claude가 응답을 거부했습니다
      Console.WriteLine("Claude was unable to process this request");
      // 요청을 다른 표현으로 바꾸거나 수정해 보세요
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("[Unsafe request]")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == "refusal" {
  	// Claude가 응답을 거부했습니다
  	fmt.Println("Claude was unable to process this request")
  	// 요청을 다른 표현으로 바꾸거나 수정해 보세요
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  Message response = client.messages().create(
      MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(1024L)
          .addUserMessage("[Unsafe request]")
          .build()
  );

  if (response.stopReason().map(StopReason.REFUSAL::equals).orElse(false)) {
      // Claude가 응답을 거부했습니다
      IO.println("Claude was unable to process this request");
      // 요청을 다른 표현으로 바꾸거나 수정해 보세요
  }
  ```

  ```php PHP
  $client = new Client();

  $response = $client->messages->create(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => '[Unsafe request]']],
      model: Model::CLAUDE_OPUS_5_5,
  );

  if ($response->stopReason === 'refusal') {
      // Claude가 응답을 거부했습니다
      echo 'Claude was unable to process this request', PHP_EOL;
      // 요청을 다른 표현으로 바꾸거나 수정해 보세요
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  response = client.messages.create(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    messages: [{ role: "user", content: "[Unsafe request]" }]
  )

  if response.stop_reason == :refusal
    # Claude가 응답을 거부했습니다
    puts "Claude was unable to process this request"
    # 요청을 다른 표현으로 바꾸거나 수정해 보세요
  end
  ```
</CodeGroup>

<Tip>
  Claude Sonnet 4.5([지원 중단](https://platform.claude.com/docs/ko/about-claude/model-deprecations)) 또는 Claude Opus 4.1(후자는 [Claude Models on Google Cloud (Google managed) 및 Amazon Bedrock을 제외하고 종료](https://platform.claude.com/docs/ko/about-claude/model-deprecations))을 사용하는 동안 `refusal` 중지 이유가 자주 발생한다면, 사용 제한이 다른 Claude Haiku 4.5(`claude-haiku-4-5-20251001`)를 사용하도록 API 호출을 업데이트해 볼 수 있습니다. [Sonnet 4.5의 API 안전 필터 이해하기](https://support.claude.com/en/articles/12449294-understanding-sonnet-4-5-s-api-safety-filters)에서 자세히 알아보세요.
</Tip>

거부 시 [`stop_details`](https://platform.claude.com/docs/ko/api/messages/create#message.stop_details) 객체는 거부를 유발한 정책 카테고리를 식별합니다. 카테고리와 전체 거부 응답 형태는 [거부의 모습](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback#refusal-response)에서 다룹니다. `stop_details`는 `refusal` 이외의 모든 중지 이유에서 `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에 적용됩니다. [거부와 폴백](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback)에서는 서버 측 또는 클라이언트에서 해당 재시도를 설정하는 방법을 보여줍니다. Claude Haiku 5.5에는 서버 측 폴백이 없으므로 재시도를 [클라이언트 측 폴백](https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback#client-side-fallback)으로 설정하세요. Claude Haiku 5.5를 제외한 이 모델들 중 하나에서 재시도를 직접 구축하는 경우, [폴백 크레딧](https://platform.claude.com/docs/ko/build-with-claude/fallback-credit)에서 프롬프트 캐시 비용을 두 번 지불하지 않는 방법을 다룹니다.

### model\_context\_window\_exceeded

Claude가 모델의 컨텍스트 윈도우 한도에 도달하여 중지했습니다. 이를 통해 정확한 입력 크기를 알지 못해도 가능한 최대 토큰을 요청할 수 있습니다.

<Note>
  이 중지 이유는 현재 SDK의 `beta` (csharp, go: `Beta`; java: `beta()`) 네임스페이스에서만 타입이 지정되어 있으므로, 다음 예제는 `client.beta.messages` (csharp, go: `client.Beta.Messages`; java: `client.beta().messages()`; php: `$client->beta->messages`)를 호출하고 `Beta` 접두사가 붙은 타입을 사용합니다. Sonnet 4.5(지원 중단) 및 이후 모델에서는 API가 베타 헤더 없이 이 값을 반환합니다. 이전 모델의 경우 이를 활성화하려면 `model-context-window-exceeded-2025-08-26` 베타 헤더를 추가하세요.
</Note>

<CodeGroup>
  ```bash cURL
  curl https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 20000,
      "messages": [{"role": "user", "content": "Large input that uses most of context window..."}]
    }'
  ```

  ```bash CLI
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 20000 \
    --message '{role: user, content: "Large input that uses most of context window..."}'
  ```

  ```python Python
  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에 도달하기 전에 "context window"(컨텍스트 윈도우) 한도에 도달했습니다
      print("Response reached model's context window limit")
      # 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한되었습니다
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  // 가능한 한 많은 출력을 얻기 위해 최대 토큰 수로 요청합니다
  const response = await client.beta.messages.create({
    model: "claude-opus-5-5",
    max_tokens: 20000,
    messages: [{ role: "user", content: "Large input that uses most of context window..." }]
  });

  if (response.stop_reason === "model_context_window_exceeded") {
    // 응답이 max_tokens에 도달하기 전에 "context window"(컨텍스트 윈도우) 한도에 도달했습니다
    console.log("Response reached model's context window limit");
    // 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한되었습니다
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  // 가능한 한 많이 받기 위해 최대 토큰 수로 요청합니다
  var response = await client.Beta.Messages.Create(new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 20000,
      Messages = [new BetaUserMessageParam { Content = "Large input that uses most of context window..." }]
  });

  if (response.StopReason?.Value() == BetaStopReason.ModelContextWindowExceeded)
  {
      // 응답이 max_tokens에 도달하기 전에 "context window"(컨텍스트 윈도우) 한도에 도달했습니다
      Console.WriteLine("Response reached model's context window limit");
      // 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한되었습니다
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  // 가능한 한 많은 출력을 얻기 위해 최대 토큰 수로 요청합니다
  response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 20000,
  	Messages: []anthropic.BetaMessageParam{
  		anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Large input that uses most of context window...")),
  	},
  })
  if err != nil {
  	log.Fatal(err)
  }

  if response.StopReason == anthropic.BetaStopReasonModelContextWindowExceeded {
  	// 응답이 max_tokens에 도달하기 전에 "context window"(컨텍스트 윈도우) 한도에 도달했습니다
  	fmt.Println("Response reached model's context window limit")
  	// 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한되었습니다
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  // 가능한 한 많은 출력을 얻기 위해 최대 토큰으로 요청합니다
  BetaMessage response = client.beta().messages().create(
      MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(20000L)
          .addUserMessage("Large input that uses most of context window...")
          .build()
  );

  if (response.stopReason().map(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED::equals).orElse(false)) {
      // 응답이 max_tokens에 도달하기 전에 "context window"(컨텍스트 윈도우) 한도에 도달했습니다
      IO.println("Response reached model's context window limit");
      // 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한되었습니다
  }
  ```

  ```php PHP
  $client = new Client();

  // 가능한 한 많은 출력을 얻기 위해 최대 토큰 수로 요청합니다
  $response = $client->beta->messages->create(
      maxTokens: 20000,
      messages: [['role' => 'user', 'content' => 'Large input that uses most of context window...']],
      model: 'claude-opus-5-5',
  );

  if ($response->stopReason === 'model_context_window_exceeded') {
      // 응답이 max_tokens에 도달하기 전에 컨텍스트 윈도우 한도에 도달했습니다
      echo 'Response reached model\'s context window limit', PHP_EOL;
      // 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한되었습니다
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  # 가능한 한 많은 출력을 얻기 위해 최대 토큰으로 요청합니다
  response = client.beta.messages.create(
    model: "claude-opus-5-5",
    max_tokens: 20000,
    messages: [{ role: "user", content: "Large input that uses most of context window..." }]
  )

  if response.stop_reason == :model_context_window_exceeded
    # 응답이 max_tokens에 도달하기 전에 "context window"(컨텍스트 윈도우) 한도에 도달했습니다
    puts "Response reached model's context window limit"
    # 응답은 여전히 유효하지만 컨텍스트 윈도우에 의해 제한되었습니다
  end
  ```
</CodeGroup>

## 중지 이유 처리를 위한 모범 사례

### 항상 stop\_reason을 확인하세요

응답 처리 로직에서 `stop_reason`을 확인하는 것을 습관화하세요:

<CodeGroup exclude="shell">
  ```python Python
  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"),
                  "",
              )
  ```

  ```typescript TypeScript
  function handleResponse(response: Anthropic.Beta.BetaMessage): string {
    switch (response.stop_reason) {
      case "tool_use":
        return handleToolUse(response);
      case "max_tokens":
        return handleTruncation(response);
      case "model_context_window_exceeded":
        return handleContextLimit(response);
      case "pause_turn":
        return handlePause(response);
      case "refusal":
        return handleRefusal(response);
      default: {
        // end_turn 및 기타 경우 처리
        const textBlock = response.content.find((block) => block.type === "text");
        return textBlock?.text ?? "";
      }
    }
  }
  ```

  ```csharp C#
  static string HandleResponse(BetaMessage response)
  {
      return response.StopReason?.Value() switch
      {
          BetaStopReason.ToolUse => HandleToolUse(response),
          BetaStopReason.MaxTokens => HandleTruncation(response),
          BetaStopReason.ModelContextWindowExceeded => HandleContextLimit(response),
          BetaStopReason.PauseTurn => HandlePause(response),
          BetaStopReason.Refusal => HandleRefusal(response),
          // end_turn 및 기타 경우 처리
          _ => response.Content.Select(b => b.Value).OfType<BetaTextBlock>().FirstOrDefault()?.Text ?? "",
      };
  }
  ```

  ```go Go
  func handleResponse(response *anthropic.BetaMessage) string {
  	switch response.StopReason {
  	case anthropic.BetaStopReasonToolUse:
  		return handleToolUse(response)
  	case anthropic.BetaStopReasonMaxTokens:
  		return handleTruncation(response)
  	case anthropic.BetaStopReasonModelContextWindowExceeded:
  		return handleContextLimit(response)
  	case anthropic.BetaStopReasonPauseTurn:
  		return handlePause(response)
  	case anthropic.BetaStopReasonRefusal:
  		return handleRefusal(response)
  	default:
  		// end_turn 및 기타 경우 처리
  		for _, block := range response.Content {
  			if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
  				return textBlock.Text
  			}
  		}
  		return ""
  	}
  }
  ```

  ```java Java
  static String handleResponse(BetaMessage response) {
      BetaStopReason reason = response.stopReason().orElse(BetaStopReason.END_TURN);
      if (reason.equals(BetaStopReason.TOOL_USE)) {
          return handleToolUse(response);
      } else if (reason.equals(BetaStopReason.MAX_TOKENS)) {
          return handleTruncation(response);
      } else if (reason.equals(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED)) {
          return handleContextLimit(response);
      } else if (reason.equals(BetaStopReason.PAUSE_TURN)) {
          return handlePause(response);
      } else if (reason.equals(BetaStopReason.REFUSAL)) {
          return handleRefusal(response);
      }
      // end_turn 및 기타 경우 처리
      return response.content().stream()
          .filter(BetaContentBlock::isText)
          .findFirst()
          .map(block -> block.asText().text())
          .orElse("");
  }
  ```

  ```php PHP
  function handle_response($response): string
  {
      return match ($response->stopReason) {
          'tool_use' => handle_tool_use($response),
          'max_tokens' => handle_truncation($response),
          'model_context_window_exceeded' => handle_context_limit($response),
          'pause_turn' => handle_pause($response),
          'refusal' => handle_refusal($response),
          // end_turn 및 기타 경우 처리
          default => array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '',
      };
  }
  ```

  ```ruby Ruby
  def handle_response(response)
    case response.stop_reason
    when :tool_use then handle_tool_use(response)
    when :max_tokens then handle_truncation(response)
    when :model_context_window_exceeded then handle_context_limit(response)
    when :pause_turn then handle_pause(response)
    when :refusal then handle_refusal(response)
    else
      # end_turn 및 기타 경우 처리
      response.content.find { it.type == :text }&.text
    end
  end
  ```
</CodeGroup>

### 잘린 응답을 우아하게 처리하세요

토큰 한도나 컨텍스트 윈도우 때문에 응답이 잘린 경우, 독자가 출력이 불완전하다는 것을 알 수 있도록 안내 문구를 덧붙이세요. 대신 응답이 끊긴 지점부터 계속 생성하려면 [완전한 응답 보장하기](https://platform.claude.com/docs/ko/build-with-claude/handling-stop-reasons#ensuring-complete-responses)를 참조하세요.

<CodeGroup exclude="shell">
  ```python Python
  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
  ```

  ```typescript TypeScript
  function handleTruncatedResponse(response: Anthropic.Beta.BetaMessage): string {
    const textBlock = response.content.find((block) => block.type === "text");
    const text = textBlock?.text ?? "";

    if (
      response.stop_reason === "max_tokens" ||
      response.stop_reason === "model_context_window_exceeded"
    ) {
      const note =
        response.stop_reason === "max_tokens"
          ? "[Response truncated due to max_tokens limit]"
          : "[Response truncated due to context window limit]";
      return `${text}\n\n${note}`;
    }
    return text;
  }
  ```

  ```csharp C#
  static string HandleTruncatedResponse(BetaMessage response)
  {
      var text = response.Content.Select(b => b.Value).OfType<BetaTextBlock>().FirstOrDefault()?.Text ?? "";
      var reason = response.StopReason?.Value();

      if (reason is BetaStopReason.MaxTokens or BetaStopReason.ModelContextWindowExceeded)
      {
          var note = reason == BetaStopReason.MaxTokens
              ? "[Response truncated due to max_tokens limit]"
              : "[Response truncated due to context window limit]";
          return $"{text}\n\n{note}";
      }
      return text;
  }
  ```

  ```go Go
  func handleTruncatedResponse(response *anthropic.BetaMessage) string {
  	text := ""
  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
  			text = textBlock.Text
  			break
  		}
  	}

  	if response.StopReason == anthropic.BetaStopReasonMaxTokens ||
  		response.StopReason == anthropic.BetaStopReasonModelContextWindowExceeded {
  		note := "[Response truncated due to context window limit]"
  		if response.StopReason == anthropic.BetaStopReasonMaxTokens {
  			note = "[Response truncated due to max_tokens limit]"
  		}
  		return text + "\n\n" + note
  	}
  	return text
  }
  ```

  ```java Java
  static String handleTruncatedResponse(BetaMessage response) {
      String text = response.content().stream()
          .filter(BetaContentBlock::isText)
          .findFirst()
          .map(block -> block.asText().text())
          .orElse("");
      BetaStopReason reason = response.stopReason().orElse(BetaStopReason.END_TURN);

      if (reason.equals(BetaStopReason.MAX_TOKENS)
              || reason.equals(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED)) {
          String note = reason.equals(BetaStopReason.MAX_TOKENS)
              ? "[Response truncated due to max_tokens limit]"
              : "[Response truncated due to context window limit]";
          return text + "\n\n" + note;
      }
      return text;
  }
  ```

  ```php PHP
  function handle_truncated_response($response): string
  {
      $text = array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '';

      if (in_array($response->stopReason, ['max_tokens', 'model_context_window_exceeded'], true)) {
          $note = $response->stopReason === 'max_tokens'
              ? '[Response truncated due to max_tokens limit]'
              : '[Response truncated due to context window limit]';
          return "{$text}\n\n{$note}";
      }
      return $text;
  }
  ```

  ```ruby Ruby
  def handle_truncated_response(response)
    text = response.content.find { it.type == :text }&.text

    if [:max_tokens, :model_context_window_exceeded].include?(response.stop_reason)
      note = if response.stop_reason == :max_tokens
        "[Response truncated due to max_tokens limit]"
      else
        "[Response truncated due to context window limit]"
      end
      return "#{text}\n\n#{note}"
    end
    text
  end
  ```
</CodeGroup>

### pause\_turn에 대한 재시도 로직을 구현하세요

[서버 도구](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/server-tools)를 사용할 때, 서버 측 샘플링 루프가 반복 한도(기본값 10)에 도달하면 API가 `pause_turn`을 반환할 수 있습니다. 이를 처리하려면 일시 중지된 응답을 변경하지 않은 채 메시지에 추가하고 요청을 다시 보내세요. 응답에 `container`가 포함되어 있으면 [해당 ID도 함께 다시 보내세요](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/code-execution-tool#container-reuse). 그렇지 않으면 다음 요청에서 새 컨테이너가 시작되며, 해당 턴의 앞부분에서 생성된 파일이 손실됩니다.

<CodeGroup exclude="shell:cURL, shell:CLI">
  <CodeGroupItem language="cURL">
    이 워크플로에는 단일 셸 명령보다 더 많은 로직이 필요합니다. 실행하려면 [Claude SDK](https://platform.claude.com/docs/ko/cli-sdks-libraries/overview)를 사용하세요.
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    이 워크플로에는 단일 셸 명령보다 더 많은 로직이 필요합니다. 실행하려면 [Claude SDK](https://platform.claude.com/docs/ko/cli-sdks-libraries/overview)를 사용하세요.
  </CodeGroupItem>

  ```python Python
  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)
  ```

  ```typescript TypeScript
  const client = new Anthropic();

  async function handleServerToolConversation(
    client: Anthropic,
    userQuery: string,
    tools: Anthropic.ToolUnion[],
    maxRequests = 5
  ): Promise<Anthropic.Message> {
    const messages: Anthropic.MessageParam[] = [{ role: "user", content: userQuery }];
    let containerId: string | null = null;
    let response: Anthropic.Message;
    let requestCount = 0;

    do {
      response = await client.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        messages,
        tools,
        container: containerId
      });

      if (response.stop_reason !== "pause_turn") {
        // 일시 중지되지 않음: 응답을 반환합니다
        return response;
      }

      // pause_turn: 일시 중지된 응답을 assistant 메시지로 추가한 후 계속합니다
      messages.push({ role: "assistant", content: response.content });

      // 도구가 컨테이너에서 실행된 경우 파일이 유지되도록 해당 ID를 다시 전송합니다
      if (response.container) {
        containerId = response.container.id;
      }
    } while (++requestCount < maxRequests);

    // maxRequests회 요청 후에도 일시 중지 상태: 마지막 응답을 반환합니다
    return response;
  }

  const response = await handleServerToolConversation(
    client,
    "Use the code execution tool to find the 1,000th prime number.",
    [{ type: "code_execution_20250825", name: "code_execution" }]
  );
  console.log(response.stop_reason);
  ```

  ```csharp C#
  // 서버 도구 턴은 몇 분 동안 실행될 수 있어 SDK의 기본 타임아웃보다 길어질 수 있습니다.
  // SDK는 이 타임아웃을 MaxTokens 값으로 설정합니다(4096일 때 약 2분).
  AnthropicClient client = new() { Timeout = TimeSpan.FromMinutes(10) };

  static async Task<Message> HandleServerToolConversation(
      AnthropicClient client,
      string userQuery,
      List<ToolUnion> tools,
      int maxRequests = 5)
  {
      List<MessageParam> messages = [new UserMessageParam { Content = userQuery }];
      MessageCreateParamsContainer? container = null;
      Message response;
      var requestCount = 0;

      do
      {
          response = await client.Messages.Create(new MessageCreateParams
          {
              Model = Model.ClaudeOpus5_5,
              MaxTokens = 4096,
              Messages = messages,
              Tools = tools,
              Container = container
          });

          if (response.StopReason != StopReason.PauseTurn)
          {
              // 일시 중지되지 않음: 응답을 반환합니다
              return response;
          }

          // pause_turn: 일시 중지된 응답을 assistant 메시지로 추가한 뒤 계속합니다
          // 요청 블록과 응답 블록은 별개의 타입이므로 각 블록의 JSON을 그대로 복사합니다
          messages.Add(new AssistantMessageParam
          {
              Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
          });

          // 도구가 컨테이너에서 실행된 경우 파일이 유지되도록 해당 ID를 다시 보냅니다
          if (response.Container is not null)
          {
              container = response.Container.ID;
          }
      } while (++requestCount < maxRequests);

      // maxRequests번 요청 후에도 일시 중지 상태이면 마지막 응답을 반환합니다
      return response;
  }

  var response = await HandleServerToolConversation(
      client,
      "Use the code execution tool to find the 1,000th prime number.",
      [new CodeExecutionTool20250825()]);
  Console.WriteLine(response.StopReason?.Raw());
  ```

  ```go Go
  func handleServerToolConversation(
  	client anthropic.Client,
  	userQuery string,
  	tools []anthropic.ToolUnionParam,
  	maxRequests int,
  ) (*anthropic.Message, error) {
  	messages := []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock(userQuery)),
  	}
  	var container anthropic.MessageCreateParamsContainerUnion
  	var response *anthropic.Message
  	var err error

  	for range maxRequests {
  		response, err = client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  			Model:     anthropic.ModelClaudeOpus5_5,
  			MaxTokens: 4096,
  			Messages:  messages,
  			Tools:     tools,
  			Container: container,
  		})
  		if err != nil {
  			return nil, err
  		}

  		if response.StopReason != anthropic.StopReasonPauseTurn {
  			// 일시 중지되지 않음: 응답을 반환합니다
  			return response, nil
  		}

  		// pause_turn: 일시 중지된 응답을 assistant 메시지로 추가한 후 계속합니다
  		messages = append(messages, response.ToParam())

  		// 도구가 컨테이너에서 실행된 경우 파일이 유지되도록 해당 ID를 다시 전송합니다
  		if response.Container.ID != "" {
  			container.OfString = anthropic.String(response.Container.ID)
  		}
  	}

  	// maxRequests번 요청 후에도 일시 중지 상태: 마지막 응답을 반환합니다
  	return response, nil
  }

  func main() {
  	client := anthropic.NewClient()

  	response, err := handleServerToolConversation(
  		client,
  		"Use the code execution tool to find the 1,000th prime number.",
  		[]anthropic.ToolUnionParam{
  			{OfCodeExecutionTool20250825: &anthropic.CodeExecutionTool20250825Param{}},
  		},
  		5,
  	)
  	if err != nil {
  		log.Fatal(err)
  	}
  	fmt.Println(response.StopReason)
  }
  ```

  ```java Java
  Message handleServerToolConversation(
      AnthropicClient client,
      String userQuery,
      List<ToolUnion> tools,
      int maxRequests
  ) {
      var params = MessageCreateParams.builder()
          .model(Model.CLAUDE_OPUS_5_5)
          .maxTokens(4096L)
          .tools(tools)
          .addUserMessage(userQuery);
      Message response = null;

      for (int request = 0; request < maxRequests; request++) {
          response = client.messages().create(params.build());

          boolean paused = response.stopReason().filter(StopReason.PAUSE_TURN::equals).isPresent();
          if (!paused) {
              // 일시 중지되지 않음: 응답을 반환합니다
              return response;
          }

          // pause_turn: 일시 중지된 응답을 assistant 메시지로 추가한 뒤 계속합니다
          params.addMessage(response);

          // 도구가 컨테이너에서 실행된 경우 파일이 유지되도록 해당 ID를 다시 보냅니다
          response.container().ifPresent(container -> params.container(container.id()));
      }

      // maxRequests번 요청 후에도 일시 중지 상태: 마지막 응답을 반환합니다
      return response;
  }

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      Message response = handleServerToolConversation(
          client,
          "Use the code execution tool to find the 1,000th prime number.",
          List.of(ToolUnion.ofCodeExecutionTool20250825(CodeExecutionTool20250825.builder().build())),
          5
      );
      response.stopReason().ifPresent(IO::println);
  }
  ```

  ```php PHP
  $client = new Client();

  function handle_server_tool_conversation(
      Client $client,
      string $userQuery,
      array $tools,
      int $maxRequests = 5,
  ): Message {
      $messages = [['role' => 'user', 'content' => $userQuery]];
      $containerId = null;
      $requestCount = 0;

      do {
          $response = $client->messages->create(
              maxTokens: 4096,
              messages: $messages,
              model: Model::CLAUDE_OPUS_5_5,
              tools: $tools,
              container: $containerId,
          );

          if ($response->stopReason !== StopReason::PAUSE_TURN->value) {
              // 일시 중지되지 않음: 응답을 반환합니다
              return $response;
          }

          // pause_turn: 일시 중지된 응답을 assistant 메시지로 추가한 후 계속합니다
          $messages[] = ['role' => 'assistant', 'content' => $response->content];

          // 도구가 컨테이너에서 실행된 경우 파일이 유지되도록 해당 ID를 다시 전송합니다
          $containerId = $response->container->id ?? $containerId;
      } while (++$requestCount < $maxRequests);

      // $maxRequests회 요청 후에도 일시 중지 상태: 마지막 응답을 반환합니다
      return $response;
  }

  $response = handle_server_tool_conversation(
      $client,
      'Use the code execution tool to find the 1,000th prime number.',
      [new CodeExecutionTool20250825()],
  );
  echo $response->stopReason, PHP_EOL;
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

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

    max_requests.times do
      response = client.messages.create(
        model: Anthropic::Model::CLAUDE_OPUS_5_5,
        max_tokens: 4096,
        messages:,
        tools:,
        container: container_id
      )

      # 일시 중지되지 않음: 응답을 반환합니다
      return response unless response.stop_reason == :pause_turn

      # pause_turn: 일시 중지된 응답을 assistant 메시지로 추가한 후 계속합니다
      messages << { role: "assistant", content: response.content }

      # 도구가 컨테이너에서 실행된 경우, 파일이 유지되도록 해당 ID를 다시 전송합니다
      container_id = response.container&.id || container_id
    end

    # max_requests회 요청 후에도 여전히 일시 중지됨: 마지막 응답을 반환합니다
    response
  end

  response = handle_server_tool_conversation(
    client,
    "Use the code execution tool to find the 1,000th prime number.",
    [Anthropic::CodeExecutionTool20250825.new]
  )
  puts response.stop_reason
  ```
</CodeGroup>

이 함수는 마지막 응답을 반환하며, 이 응답에는 턴의 마지막 부분만 포함됩니다. 전체 턴을 표시하거나 대화를 계속하려면 일시 중지된 응답도 함께 보관하세요.

## 중지 이유와 오류의 비교

`stop_reason` 값과 실제 오류를 구분하는 것이 중요합니다:

### 중지 이유 (성공적인 응답)

* 응답 본문의 일부
* 생성이 정상적으로 중지된 이유를 나타냄
* 응답에 유효한 콘텐츠가 포함됨

### 오류 (실패한 요청)

* HTTP 상태 코드 4xx 또는 5xx
* 요청 처리 실패를 나타냄
* 응답에 오류 세부 정보가 포함됨

<CodeGroup>
  ```bash cURL
  # `--fail-with-body` 옵션을 사용하면 cURL은 HTTP 오류 시 0이 아닌 코드로 종료됩니다. 따라서
  # 오류는 $? 값을, 성공한 응답은 stop_reason 값을 확인하세요.
  curl --fail-with-body https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "messages": [{"role": "user", "content": "Hello!"}]
    }'
  ```

  ```bash CLI
  # CLI는 API 오류 시 0이 아닌 코드로 종료되며, 성공 시에는 stop_reason이 표시됩니다.
  ant messages create \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello!"}'
  ```

  ```python Python
  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")
  ```

  ```typescript TypeScript
  const client = new Anthropic();

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

    // stop_reason이 포함된 성공 응답을 처리합니다
    if (response.stop_reason === "max_tokens") {
      console.log("Response was truncated");
    }
  } catch (err) {
    // 실제 오류를 처리합니다
    if (err instanceof Anthropic.APIError) {
      switch (err.status) {
        case 429:
          console.log("Rate limit exceeded");
          break;
        case 500:
          console.log("Server error");
          break;
      }
    } else {
      throw err;
    }
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  try
  {
      var response = await client.Messages.Create(new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 1024,
          Messages = [new UserMessageParam { Content = "Hello!" }]
      });

      // stop_reason이 포함된 성공 응답을 처리합니다
      if (response.StopReason == "max_tokens")
      {
          Console.WriteLine("Response was truncated");
      }
  }
  catch (AnthropicRateLimitException)
  {
      // 실제 오류를 처리합니다
      Console.WriteLine("Rate limit exceeded");
  }
  catch (Anthropic5xxException)
  {
      Console.WriteLine("Server error");
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello!")),
  	},
  })
  if err != nil {
  	// 실제 오류를 처리합니다
  	var apiErr *anthropic.Error
  	if errors.As(err, &apiErr) {
  		switch apiErr.StatusCode {
  		case 429:
  			fmt.Println("Rate limit exceeded")
  		case 500:
  			fmt.Println("Server error")
  		}
  	}
  	log.Fatal(err)
  }

  // stop_reason이 포함된 성공 응답을 처리합니다
  if response.StopReason == "max_tokens" {
  	fmt.Println("Response was truncated")
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  try {
      Message response = client.messages().create(
          MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .addUserMessage("Hello!")
              .build()
      );

      // stop_reason이 포함된 성공 응답을 처리합니다
      if (response.stopReason().map(StopReason.MAX_TOKENS::equals).orElse(false)) {
          IO.println("Response was truncated");
      }
  } catch (RateLimitException e) {
      // 실제 오류를 처리합니다
      IO.println("Rate limit exceeded");
  } catch (AnthropicServiceException e) {
      if (e.statusCode() == 500) {
          IO.println("Server error");
      }
  }
  ```

  ```php PHP
  $client = new Client();

  try {
      $response = $client->messages->create(
          maxTokens: 1024,
          messages: [['role' => 'user', 'content' => 'Hello!']],
          model: Model::CLAUDE_OPUS_5_5,
      );

      // stop_reason이 포함된 성공 응답을 처리합니다
      if ($response->stopReason === 'max_tokens') {
          echo 'Response was truncated', PHP_EOL;
      }
  } catch (RateLimitException $e) {
      // 실제 오류를 처리합니다
      echo 'Rate limit exceeded', PHP_EOL;
  } catch (InternalServerException $e) {
      echo 'Server error', PHP_EOL;
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  begin
    response = client.messages.create(
      model: Anthropic::Model::CLAUDE_OPUS_5_5,
      max_tokens: 1024,
      messages: [{ role: "user", content: "Hello!" }]
    )

    # stop_reason이 포함된 성공 응답을 처리합니다
    if response.stop_reason == :max_tokens
      puts "Response was truncated"
    end
  rescue Anthropic::Errors::RateLimitError
    # 실제 오류를 처리합니다
    puts "Rate limit exceeded"
  rescue Anthropic::Errors::APIStatusError => e
    puts "Server error" if e.status == 500
  end
  ```
</CodeGroup>

## 스트리밍 고려 사항

스트리밍을 사용할 때 `stop_reason`은:

* 초기 `message_start` 이벤트에서는 `null`
* `message_delta` 이벤트에서 제공됨
* 다른 어떤 이벤트에서도 제공되지 않음

<CodeGroup>
  ```bash cURL
  # SSE 스트림의 message_delta 이벤트에 stop_reason이 포함됩니다.
  curl -N https://api.anthropic.com/v1/messages \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "content-type: application/json" \
    -d '{
      "model": "claude-opus-5-5",
      "max_tokens": 1024,
      "stream": true,
      "messages": [{"role": "user", "content": "Hello!"}]
    }'
  ```

  ```bash CLI
  # stop_reason 은 message_delta 이벤트에 나타납니다.
  ant messages create --stream \
    --model claude-opus-5-5 \
    --max-tokens 1024 \
    --message '{role: user, content: "Hello!"}'
  ```

  ```python Python
  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}")
  ```

  ```typescript TypeScript
  const client = new Anthropic();

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

  for await (const event of stream) {
    if (event.type === "message_delta" && event.delta.stop_reason) {
      console.log(`Stream ended with: ${event.delta.stop_reason}`);
    }
  }
  ```

  ```csharp C#
  AnthropicClient client = new();

  var parameters = new MessageCreateParams
  {
      Model = Model.ClaudeOpus5_5,
      MaxTokens = 1024,
      Messages = [new UserMessageParam { Content = "Hello!" }]
  };

  await foreach (var streamEvent in client.Messages.CreateStreaming(parameters))
  {
      switch (streamEvent.Value)
      {
          case RawMessageDeltaEvent deltaEvent when deltaEvent.Delta.StopReason is not null:
              Console.WriteLine($"Stream ended with: {deltaEvent.Delta.StopReason}");
              break;
      }
  }
  ```

  ```go Go
  client := anthropic.NewClient()

  stream := client.Messages.NewStreaming(context.TODO(), anthropic.MessageNewParams{
  	Model:     anthropic.ModelClaudeOpus5_5,
  	MaxTokens: 1024,
  	Messages: []anthropic.MessageParam{
  		anthropic.NewUserMessage(anthropic.NewTextBlock("Hello!")),
  	},
  })

  // 이벤트를 누적하여 stop_reason을 포함하는 최종 Message를 만듭니다.
  message := anthropic.Message{}
  for stream.Next() {
  	if err := message.Accumulate(stream.Current()); err != nil {
  		log.Fatal(err)
  	}
  }
  if err := stream.Err(); err != nil {
  	log.Fatal(err)
  }

  if message.StopReason != "" {
  	fmt.Printf("Stream ended with: %s\n", message.StopReason)
  }
  ```

  ```java Java
  AnthropicClient client = AnthropicOkHttpClient.fromEnv();

  MessageCreateParams params = MessageCreateParams.builder()
      .model(Model.CLAUDE_OPUS_5_5)
      .maxTokens(1024L)
      .addUserMessage("Hello!")
      .build();

  // 이벤트를 누적하여 stop_reason을 포함하는 최종 Message를 만듭니다.
  MessageAccumulator accumulator = MessageAccumulator.create();
  try (StreamResponse<RawMessageStreamEvent> streamResponse =
          client.messages().createStreaming(params)) {
      streamResponse.stream().forEach(accumulator::accumulate);
  }

  accumulator.message().stopReason().ifPresent(stopReason ->
      IO.println("Stream ended with: " + stopReason)
  );
  ```

  ```php PHP
  $client = new Client();

  $stream = $client->messages->createStream(
      maxTokens: 1024,
      messages: [['role' => 'user', 'content' => 'Hello!']],
      model: Model::CLAUDE_OPUS_5_5,
  );

  foreach ($stream as $event) {
      if ($event instanceof \Anthropic\Messages\RawMessageDeltaEvent && $event->delta->stopReason !== null) {
          echo "Stream ended with: {$event->delta->stopReason}", PHP_EOL;
      }
  }
  ```

  ```ruby Ruby
  client = Anthropic::Client.new

  stream = client.messages.stream(
    model: Anthropic::Model::CLAUDE_OPUS_5_5,
    max_tokens: 1024,
    messages: [{ role: "user", content: "Hello!" }]
  )

  stream.each do |event|
    next unless event.type == :message_delta
    stop_reason = event.delta.stop_reason
    puts "Stream ended with: #{stop_reason}" if stop_reason
  end
  ```
</CodeGroup>

## 일반적인 패턴

### 도구 사용 워크플로 처리하기

<Tip>
  **도구 러너로 더 간단하게:** 다음 예제는 수동 도구 처리를 보여줍니다. 대부분의 사용 사례에서는 [도구 러너](https://platform.claude.com/docs/ko/agents-and-tools/tool-use/tool-runner)가 훨씬 적은 코드로 도구 실행을 자동으로 처리합니다.
</Tip>

<CodeGroup exclude="shell">
  ```python Python
  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
  ```

  ```typescript TypeScript
  async function completeToolWorkflow(
    client: Anthropic,
    userQuery: string,
    tools: Anthropic.ToolUnion[]
  ): Promise<Anthropic.Message> {
    const messages: Anthropic.MessageParam[] = [{ role: "user", content: userQuery }];

    while (true) {
      const response = await client.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 1024,
        messages,
        tools
      });

      if (response.stop_reason === "tool_use") {
        // 도구를 실행하고 계속 진행합니다
        const toolResults = executeTools(response.content);
        messages.push({ role: "assistant", content: response.content });
        messages.push({ role: "user", content: toolResults });
      } else {
        // 최종 응답
        return response;
      }
    }
  }
  ```

  ```csharp C#
  static async Task<Message> CompleteToolWorkflow(
      AnthropicClient client,
      string userQuery,
      List<ToolUnion> tools)
  {
      List<MessageParam> messages = [new UserMessageParam { Content = userQuery }];

      while (true)
      {
          var response = await client.Messages.Create(new MessageCreateParams
          {
              Model = Model.ClaudeOpus5_5,
              MaxTokens = 1024,
              Messages = messages,
              Tools = tools
          });

          if (response.StopReason == "tool_use")
          {
              // 도구를 실행하고 계속 진행
              var toolResults = ExecuteTools(response.Content);
              messages.Add(new AssistantMessageParam
              {
                  Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
              });
              messages.Add(new UserMessageParam { Content = toolResults });
          }
          else
          {
              // 최종 응답
              return response;
          }
      }
  }
  ```

  ```go Go
  func completeToolWorkflow(
  	client anthropic.Client,
  	userQuery string,
  	tools []anthropic.ToolUnionParam,
  ) (*anthropic.Message, error) {
  	messages := []anthropic.MessageParam{anthropic.NewUserMessage(anthropic.NewTextBlock(userQuery))}

  	for {
  		response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  			Model:     anthropic.ModelClaudeOpus5_5,
  			MaxTokens: 1024,
  			Messages:  messages,
  			Tools:     tools,
  		})
  		if err != nil {
  			return nil, err
  		}

  		if response.StopReason != "tool_use" {
  			// 최종 응답
  			return response, nil
  		}

  		// 도구를 실행하고 계속 진행
  		toolResults := executeTools(response.Content)
  		var contentParams []anthropic.ContentBlockParamUnion
  		for _, block := range response.Content {
  			contentParams = append(contentParams, block.ToParam())
  		}
  		messages = append(messages, anthropic.NewAssistantMessage(contentParams...))
  		messages = append(messages, anthropic.NewUserMessage(toolResults...))
  	}
  }
  ```

  ```java Java
  static Message completeToolWorkflow(
      AnthropicClient client,
      String userQuery,
      List<Tool> tools
  ) {
      List<MessageParam> messages = new ArrayList<>();
      messages.add(MessageParam.ofUser(userQuery));

      while (true) {
          MessageCreateParams.Builder params = MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(1024L)
              .messages(messages);
          tools.forEach(params::addTool);

          Message response = client.messages().create(params.build());

          if (!response.stopReason().map(StopReason.TOOL_USE::equals).orElse(false)) {
              // 최종 응답
              return response;
          }

          // 도구를 실행하고 계속 진행
          List<ToolResultBlockParam> toolResults = executeTools(response.content());
          messages.add(response.toParam());
          messages.add(MessageParam.ofUserOfContentBlockParams(
              toolResults.stream().map(ContentBlockParam::ofToolResult).toList()));
      }
  }
  ```

  ```php PHP
  function complete_tool_workflow(Client $client, string $userQuery, array $tools)
  {
      $messages = [['role' => 'user', 'content' => $userQuery]];

      while (true) {
          $response = $client->messages->create(
              maxTokens: 1024,
              messages: $messages,
              model: 'claude-opus-5-5',
              tools: $tools,
          );

          if ($response->stopReason !== 'tool_use') {
              // 최종 응답
              return $response;
          }

          // 도구를 실행하고 계속 진행
          $toolResults = execute_tools($response->content);
          $messages[] = ['role' => 'assistant', 'content' => $response->content];
          $messages[] = ['role' => 'user', 'content' => $toolResults];
      }
  }
  ```

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

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

      # 최종 응답
      return response unless response.stop_reason == :tool_use

      # 도구를 실행하고 계속 진행
      tool_results = execute_tools(response.content)
      messages << { role: "assistant", content: response.content }
      messages << { role: "user", content: tool_results }
    end
  end
  ```
</CodeGroup>

### 완전한 응답 보장하기

<CodeGroup exclude="shell">
  ```python Python
  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
  ```

  ```typescript TypeScript
  async function getCompleteResponse(
    client: Anthropic,
    prompt: string,
    maxAttempts = 3
  ): Promise<string> {
    let messages: Anthropic.MessageParam[] = [{ role: "user", content: prompt }];
    let fullResponse = "";

    for (let i = 0; i < maxAttempts; i++) {
      const response = await client.messages.create({
        model: "claude-opus-5-5",
        max_tokens: 4096,
        messages
      });

      const textBlock = response.content.find((block) => block.type === "text");
      fullResponse += textBlock?.text ?? "";

      if (response.stop_reason !== "max_tokens") {
        break;
      }

      // 중단된 지점부터 이어서 진행합니다
      messages = [
        { role: "user", content: prompt },
        { role: "assistant", content: fullResponse },
        { role: "user", content: "Please continue from where you left off." }
      ];
    }

    return fullResponse;
  }
  ```

  ```csharp C#
  static async Task<string> GetCompleteResponse(AnthropicClient client, string prompt, int maxAttempts = 3)
  {
      List<MessageParam> messages = [new UserMessageParam { Content = prompt }];
      var fullResponse = "";

      for (var i = 0; i < maxAttempts; i++)
      {
          var response = await client.Messages.Create(new MessageCreateParams
          {
              Model = Model.ClaudeOpus5_5,
              MaxTokens = 4096,
              Messages = messages
          });

          foreach (var block in response.Content)
          {
              if (block.TryPickText(out var textBlock))
              {
                  fullResponse += textBlock.Text;
                  break;
              }
          }

          if (response.StopReason != "max_tokens")
          {
              break;
          }

          // 중단된 지점부터 계속합니다
          messages =
          [
              new UserMessageParam { Content = prompt },
              new AssistantMessageParam { Content = fullResponse },
              new UserMessageParam { Content = "Please continue from where you left off." }
          ];
      }

      return fullResponse;
  }
  ```

  ```go Go
  func getCompleteResponse(client anthropic.Client, prompt string, maxAttempts int) (string, error) {
  	messages := []anthropic.MessageParam{anthropic.NewUserMessage(anthropic.NewTextBlock(prompt))}
  	fullResponse := ""

  	for range maxAttempts {
  		response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
  			Model:     anthropic.ModelClaudeOpus5_5,
  			MaxTokens: 4096,
  			Messages:  messages,
  		})
  		if err != nil {
  			return "", err
  		}

  		for _, block := range response.Content {
  			if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
  				fullResponse += textBlock.Text
  				break
  			}
  		}

  		if response.StopReason != "max_tokens" {
  			break
  		}

  		// 중단된 지점부터 이어서 진행합니다
  		messages = []anthropic.MessageParam{
  			anthropic.NewUserMessage(anthropic.NewTextBlock(prompt)),
  			anthropic.NewAssistantMessage(anthropic.NewTextBlock(fullResponse)),
  			anthropic.NewUserMessage(anthropic.NewTextBlock("Please continue from where you left off.")),
  		}
  	}

  	return fullResponse, nil
  }
  ```

  ```java Java
  static String getCompleteResponse(AnthropicClient client, String prompt, int maxAttempts) {
      List<MessageParam> messages = List.of(MessageParam.ofUser(prompt));
      StringBuilder fullResponse = new StringBuilder();

      for (int i = 0; i < maxAttempts; i++) {
          Message response = client.messages().create(
              MessageCreateParams.builder()
                  .model(Model.CLAUDE_OPUS_5_5)
                  .maxTokens(4096L)
                  .messages(messages)
                  .build()
          );

          response.content().stream()
              .filter(ContentBlock::isText)
              .findFirst()
              .ifPresent(block -> fullResponse.append(block.asText().text()));

          if (!response.stopReason().map(StopReason.MAX_TOKENS::equals).orElse(false)) {
              break;
          }

          // 중단된 지점부터 이어서 진행합니다
          messages = List.of(
              MessageParam.ofUser(prompt),
              MessageParam.ofAssistant(fullResponse.toString()),
              MessageParam.ofUser("Please continue from where you left off.")
          );
      }

      return fullResponse.toString();
  }
  ```

  ```php PHP
  function get_complete_response(Client $client, string $prompt, int $maxAttempts = 3): string
  {
      $messages = [['role' => 'user', 'content' => $prompt]];
      $fullResponse = '';

      for ($i = 0; $i < $maxAttempts; $i++) {
          $response = $client->messages->create(
              maxTokens: 4096,
              messages: $messages,
              model: 'claude-opus-5-5',
          );

          $fullResponse .= array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '';

          if ($response->stopReason !== 'max_tokens') {
              break;
          }

          // 중단된 지점부터 계속합니다
          $messages = [
              ['role' => 'user', 'content' => $prompt],
              ['role' => 'assistant', 'content' => $fullResponse],
              ['role' => 'user', 'content' => 'Please continue from where you left off.'],
          ];
      }

      return $fullResponse;
  }
  ```

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

    max_attempts.times do
      response = client.messages.create(
        model: "claude-opus-5-5",
        max_tokens: 4096,
        messages: messages
      )

      full_response << response.content.find { it.type == :text }&.text.to_s

      break unless response.stop_reason == :max_tokens

      # 중단된 지점부터 이어서 계속합니다
      messages = [
        { role: "user", content: prompt },
        { role: "assistant", content: full_response },
        { role: "user", content: "Please continue from where you left off." }
      ]
    end

    full_response
  end
  ```
</CodeGroup>

### 입력 크기를 모르는 상태에서 최대 토큰 얻기

`model_context_window_exceeded` 중지 이유를 사용하면 입력 크기를 계산하지 않고도 가능한 최대 토큰을 요청할 수 있습니다:

<CodeGroup exclude="shell">
  ```python Python
  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"), "")
  ```

  ```typescript TypeScript
  async function getMaxPossibleTokens(client: Anthropic, prompt: string): Promise<string> {
    const response = await client.beta.messages.create({
      model: "claude-opus-5-5",
      max_tokens: 20000,
      messages: [{ role: "user", content: prompt }]
    });

    const tokens = response.usage.output_tokens;
    switch (response.stop_reason) {
      case "model_context_window_exceeded":
        // 입력 크기를 고려해 가능한 최대 토큰 수를 받았습니다
        console.log(`Generated ${tokens} tokens (context limit reached)`);
        break;
      case "max_tokens":
        // 요청한 토큰 수를 정확히 받았습니다
        console.log(`Generated ${tokens} tokens (max_tokens reached)`);
        break;
      default:
        // 자연스럽게 완료되었습니다
        console.log(`Generated ${tokens} tokens (natural completion)`);
    }

    const textBlock = response.content.find((block) => block.type === "text");
    return textBlock?.text ?? "";
  }
  ```

  ```csharp C#
  using Anthropic.Models.Beta.Messages;
  using Model = Anthropic.Models.Messages.Model;

  static async Task<string> GetMaxPossibleTokens(AnthropicClient client, string prompt)
  {
      var response = await client.Beta.Messages.Create(new MessageCreateParams
      {
          Model = Model.ClaudeOpus5_5,
          MaxTokens = 20000,
          Messages = [new BetaUserMessageParam { Content = prompt }]
      });

      var tokens = response.Usage.OutputTokens;
      var reason = response.StopReason?.Value();
      if (reason == BetaStopReason.ModelContextWindowExceeded)
      {
          // 입력 크기를 고려할 때 가능한 최대 토큰 수를 받았습니다
          Console.WriteLine($"Generated {tokens} tokens (context limit reached)");
      }
      else if (reason == BetaStopReason.MaxTokens)
      {
          // 요청한 토큰 수를 정확히 받았습니다
          Console.WriteLine($"Generated {tokens} tokens (max_tokens reached)");
      }
      else
      {
          // 자연스러운 완료
          Console.WriteLine($"Generated {tokens} tokens (natural completion)");
      }

      return response.Content.Select(b => b.Value).OfType<BetaTextBlock>().FirstOrDefault()?.Text ?? "";
  }
  ```

  ```go Go
  func getMaxPossibleTokens(client anthropic.Client, prompt string) (string, error) {
  	response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
  		Model:     anthropic.ModelClaudeOpus5_5,
  		MaxTokens: 20000,
  		Messages: []anthropic.BetaMessageParam{
  			anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock(prompt)),
  		},
  	})
  	if err != nil {
  		return "", err
  	}

  	tokens := response.Usage.OutputTokens
  	switch response.StopReason {
  	case anthropic.BetaStopReasonModelContextWindowExceeded:
  		// 입력 크기를 고려해 가능한 최대 토큰 수를 받았습니다
  		fmt.Printf("Generated %d tokens (context limit reached)\n", tokens)
  	case anthropic.BetaStopReasonMaxTokens:
  		// 요청한 토큰 수를 정확히 받았습니다
  		fmt.Printf("Generated %d tokens (max_tokens reached)\n", tokens)
  	default:
  		// 자연스럽게 완료되었습니다
  		fmt.Printf("Generated %d tokens (natural completion)\n", tokens)
  	}

  	for _, block := range response.Content {
  		if textBlock, ok := block.AsAny().(anthropic.BetaTextBlock); ok {
  			return textBlock.Text, nil
  		}
  	}
  	return "", nil
  }
  ```

  ```java Java
  import com.anthropic.models.beta.messages.BetaContentBlock;
  import com.anthropic.models.beta.messages.BetaMessage;
  import com.anthropic.models.beta.messages.BetaStopReason;
  import com.anthropic.models.beta.messages.MessageCreateParams;

  static String getMaxPossibleTokens(AnthropicClient client, String prompt) {
      BetaMessage response = client.beta().messages().create(
          MessageCreateParams.builder()
              .model(Model.CLAUDE_OPUS_5_5)
              .maxTokens(20000L)
              .addUserMessage(prompt)
              .build()
      );

      long tokens = response.usage().outputTokens();
      BetaStopReason reason = response.stopReason().orElse(BetaStopReason.END_TURN);
      if (reason.equals(BetaStopReason.MODEL_CONTEXT_WINDOW_EXCEEDED)) {
          // 입력 크기를 고려할 때 가능한 최대 토큰 수를 받았습니다
          IO.println("Generated " + tokens + " tokens (context limit reached)");
      } else if (reason.equals(BetaStopReason.MAX_TOKENS)) {
          // 요청한 토큰 수를 정확히 받았습니다
          IO.println("Generated " + tokens + " tokens (max_tokens reached)");
      } else {
          // 자연스러운 완료
          IO.println("Generated " + tokens + " tokens (natural completion)");
      }

      return response.content().stream()
          .filter(BetaContentBlock::isText)
          .findFirst()
          .map(block -> block.asText().text())
          .orElse("");
  }
  ```

  ```php PHP
  function get_max_possible_tokens(Client $client, string $prompt): string
  {
      $response = $client->beta->messages->create(
          maxTokens: 20000,
          messages: [['role' => 'user', 'content' => $prompt]],
          model: 'claude-opus-5-5',
      );

      $tokens = $response->usage->outputTokens;
      echo match ($response->stopReason) {
          // 입력 크기를 고려해 가능한 최대 토큰 수를 받았습니다
          'model_context_window_exceeded' => "Generated {$tokens} tokens (context limit reached)",
          // 요청한 토큰 수를 정확히 받았습니다
          'max_tokens' => "Generated {$tokens} tokens (max_tokens reached)",
          // 자연스러운 완료
          default => "Generated {$tokens} tokens (natural completion)",
      }, PHP_EOL;

      return array_find($response->content, static fn ($block): bool => $block->type === 'text')?->text ?? '';
  }
  ```

  ```ruby Ruby
  def get_max_possible_tokens(client, prompt)
    response = client.beta.messages.create(
      model: "claude-opus-5-5",
      max_tokens: 20000,
      messages: [{ role: "user", content: prompt }]
    )

    tokens = response.usage.output_tokens
    case response.stop_reason
    when :model_context_window_exceeded
      # 입력 크기를 고려해 가능한 최대 토큰 수를 받았습니다
      puts "Generated #{tokens} tokens (context limit reached)"
    when :max_tokens
      # 요청한 토큰 수를 정확히 받았습니다
      puts "Generated #{tokens} tokens (max_tokens reached)"
    else
      # 자연스러운 완료
      puts "Generated #{tokens} tokens (natural completion)"
    end

    response.content.find { it.type == :text }.text
  end
  ```
</CodeGroup>

## 다음 단계

<CardGroup cols={2}>
  <Card title="거부와 폴백" icon="arrows-clockwise" href="https://platform.claude.com/docs/ko/build-with-claude/refusals-and-fallback">
    거부된 요청을 서버 측 또는 클라이언트에서 폴백 모델로 재시도하세요.
  </Card>

  <Card title="도구 러너 (SDK)" icon="wrench" href="https://platform.claude.com/docs/ko/agents-and-tools/tool-use/tool-runner">
    SDK가 `tool_use` 루프, 결과 포맷팅, 재시도를 대신 관리하도록 하세요.
  </Card>

  <Card title="메시지 스트리밍" icon="lightning" href="https://platform.claude.com/docs/ko/build-with-claude/streaming">
    스트리밍 시 `message_delta` 이벤트에서 `stop_reason`을 읽으세요.
  </Card>

  <Card title="오류" icon="info" href="https://platform.claude.com/docs/ko/api/errors">
    중지 이유와는 구별되는 4xx 및 5xx HTTP 오류를 처리하세요.
  </Card>
</CardGroup>
