Loading...
    • Developer Guide
    • API Reference
    • MCP
    • Resources
    • Release Notes
    Search...
    ⌘K
    First steps
    Intro to ClaudeQuickstart
    Models & pricing
    Models overviewChoosing a modelWhat's new in Claude 4.6Migration guideModel deprecationsPricing
    Build with Claude
    Features overviewUsing the Messages APIHandling stop reasonsPrompting best practices
    Context management
    Context windowsCompactionContext editing
    Capabilities
    Prompt cachingExtended thinkingAdaptive thinkingEffortFast mode (research preview)Streaming MessagesBatch processingCitationsMultilingual supportToken countingEmbeddingsVisionPDF supportFiles APISearch resultsStructured outputs
    Tools
    OverviewHow to implement tool useFine-grained tool streamingBash toolCode execution toolProgrammatic tool callingComputer use toolText editor toolWeb fetch toolWeb search toolMemory toolTool search tool
    Agent Skills
    OverviewQuickstartBest practicesSkills for enterpriseUsing Skills with the API
    Agent SDK
    OverviewQuickstartTypeScript SDKTypeScript V2 (preview)Python SDKMigration Guide
    Streaming InputStream responses in real-timeHandling stop reasonsHandling PermissionsUser approvals and inputControl execution with hooksSession ManagementFile checkpointingStructured outputs in the SDKHosting the Agent SDKSecurely deploying AI agentsModifying system promptsMCP in the SDKCustom ToolsSubagents in the SDKSlash Commands in the SDKAgent Skills in the SDKTracking Costs and UsageTodo ListsPlugins in the SDK
    MCP in the API
    MCP connectorRemote MCP servers
    Claude on 3rd-party platforms
    Amazon BedrockMicrosoft FoundryVertex AI
    Prompt engineering
    OverviewPrompt generatorUse prompt templatesPrompt improverBe clear and directUse examples (multishot prompting)Let Claude think (CoT)Use XML tagsGive Claude a role (system prompts)Chain complex promptsLong context tipsExtended thinking tips
    Test & evaluate
    Define success criteriaDevelop test casesUsing the Evaluation ToolReducing latency
    Strengthen guardrails
    Reduce hallucinationsIncrease output consistencyMitigate jailbreaksStreaming refusalsReduce prompt leakKeep Claude in character
    Administration and monitoring
    Admin API overviewData residencyWorkspacesUsage and Cost APIClaude Code Analytics APIZero Data Retention
    Console
    Log in
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...
    Loading...

    Solutions

    • AI agents
    • Code modernization
    • Coding
    • Customer support
    • Education
    • Financial services
    • Government
    • Life sciences

    Partners

    • Amazon Bedrock
    • Google Cloud's Vertex AI

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Company

    • Anthropic
    • Careers
    • Economic Futures
    • Research
    • News
    • Responsible Scaling Policy
    • Security and compliance
    • Transparency

    Learn

    • Blog
    • Catalog
    • Courses
    • Use cases
    • Connectors
    • Customer stories
    • Engineering at Anthropic
    • Events
    • Powered by Claude
    • Service partners
    • Startups program

    Help and security

    • Availability
    • Status
    • Support
    • Discord

    Terms and policies

    • Privacy policy
    • Responsible disclosure policy
    • Terms of service: Commercial
    • Terms of service: Consumer
    • Usage policy
    Guides

    Handling stop reasons

    Detect refusals and other stop reasons directly from result messages in the Agent SDK

    The stop_reason field on result messages tells you why the model stopped generating. This is the recommended way to detect refusals, max-token limits, and other termination conditions (no stream parsing required).

    stop_reason is available on every ResultMessage, regardless of whether streaming is enabled. You don't need to set include_partial_messages (Python) or includePartialMessages (TypeScript).

    Reading stop_reason

    The stop_reason field is present on both success and error result messages. Check it after iterating through the message stream:

    from claude_agent_sdk import query, ResultMessage
    import asyncio
    
    async def check_stop_reason():
        async for message in query(prompt="Write a poem about the ocean"):
            if isinstance(message, ResultMessage):
                print(f"Stop reason: {message.stop_reason}")
                if message.stop_reason == "refusal":
                    print("The model declined this request.")
    
    asyncio.run(check_stop_reason())

    Available stop reasons

    Stop reasonMeaning
    end_turnThe model finished generating its response normally.
    max_tokensThe response reached the maximum output token limit.
    stop_sequenceThe model generated a configured stop sequence.
    refusalThe model declined to fulfill the request.
    tool_useThe model's final output was a tool call. This is uncommon in SDK results because tool calls are normally executed before the result is returned.
    nullNo API response was received; for example, an error occurred before the first request, or the result was replayed from a cached session.

    Stop reasons on error results

    Error results (such as error_max_turns or error_during_execution) also carry stop_reason. The value reflects the last assistant message received before the error occurred:

    Result variantstop_reason value
    successThe stop reason from the final assistant message.
    error_max_turnsThe stop reason from the last assistant message before the turn limit was hit.
    error_max_budget_usdThe stop reason from the last assistant message before the budget was exceeded.
    error_max_structured_output_retriesThe stop reason from the last assistant message before the retry limit was hit.
    error_during_executionThe last stop reason seen, or null if the error occurred before any API response.
    from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
    import asyncio
    
    async def handle_max_turns():
        options = ClaudeAgentOptions(max_turns=3)
    
        async for message in query(prompt="Refactor this module", options=options):
            if isinstance(message, ResultMessage):
                if message.subtype == "error_max_turns":
                    print(f"Hit turn limit. Last stop reason: {message.stop_reason}")
                    # stop_reason might be "end_turn" or "tool_use"
                    # depending on what the model was doing when the limit hit
    
    asyncio.run(handle_max_turns())

    Detecting refusals

    stop_reason === "refusal" is the simplest way to detect when the model declines a request. Previously, detecting refusals required enabling partial message streaming and manually scanning StreamEvent messages for message_delta events. With stop_reason on the result message, you can check directly:

    from claude_agent_sdk import query, ResultMessage
    import asyncio
    
    async def safe_query(prompt: str):
        async for message in query(prompt=prompt):
            if isinstance(message, ResultMessage):
                if message.stop_reason == "refusal":
                    print("Request was declined. Please revise your prompt.")
                    return None
                return message.result
        return None
    
    asyncio.run(safe_query("Summarize this article"))

    Next steps

    • Stream responses in real-time: access raw API events including message_delta as they arrive
    • Structured outputs: get typed JSON responses from the agent
    • Tracking costs and usage: understand token usage and billing from result messages

    Was this page helpful?

    • Reading stop_reason
    • Available stop reasons
    • Stop reasons on error results
    • Detecting refusals
    • Next steps