Skip to content

fix(openai): report max_tokens when a Responses function call is cut off - #4139

Merged
liramon2 merged 1 commit into
strands-agents:mainfrom
BlueX888:fix/openai-responses-max-tokens-tool-call
Sep 3, 2026
Merged

liramon2 merged 1 commit into
strands-agents:mainfrom
BlueX888:fix/openai-responses-max-tokens-tool-call

Conversation

@BlueX888

@BlueX888 BlueX888 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Description

When the OpenAI Responses API returns response.incomplete with incomplete_details.reason == "max_output_tokens" after a function_call output item has started, OpenAIResponsesModel reported stopReason: "tool_use" instead of "max_tokens". The event loop then executed the truncated tool call (with input: {} because the partial JSON does not parse) instead of running recover_message_on_max_tokens_reached and raising MaxTokensReachedException.

Root cause: tool_calls is populated on response.output_item.added, i.e. before any arguments have streamed, and the finish-reason mapping gave tool_calls precedence over the length stop reason. So any function call in flight when the response was cut off won over length.

Changes:

  • openai_responses.py: check stop_reason == "length" before tool_calls when computing finish_reason. This matches OpenAIModel (Chat Completions), which forwards finish_reason == "length" regardless of accumulated tool-call deltas.
  • test_openai_responses.py: add a regression test for a function call cut off by max_output_tokens, asserting max_tokens is reported and tool_use is not.

Note: #3419 contains the same two-line reordering as part of a much larger change. This PR isolates the stop-reason fix so it can land on its own; the resulting code in that block is identical, so there is no conflict in outcome.

The TypeScript Responses adapter has the same precedence (responses-adapter.ts, stopReason = 'toolUse' after maxTokens was set). I left it out to keep this PR focused and can follow up separately.

Related Issues

Fixes #4135

Documentation PR

No documentation change needed.

Type of Change

Bug fix

Testing

  • pytest tests/strands/models/test_openai_responses.py: 137 passed (136 existing + 1 new).

  • New test fails on main without the source change and passes with it.

  • pytest tests/strands/event_loop: 173 passed.

  • ruff format --check, ruff check, mypy on the two changed files: clean.

  • Full tests/ run: 5174 passed; the 2 failures in test_mcp_client.py::test_mcp_client_client_info_passed also fail on unmodified upstream/main in my environment, and the collection errors are optional-dependency modules (a2a, writer, cedar) not installed in my env.

  • Repro from [BUG] OpenAIResponsesModel reports tool_use instead of max_tokens when a function call is cut off by max_output_tokens #4135 now prints messageStop: [{'messageStop': {'stopReason': 'max_tokens'}}] and the agent raises MaxTokensReachedException instead of invoking the tool with empty input.

  • I ran hatch run prepare (hatch is not installed locally; I ran the equivalent ruff/mypy/pytest commands above)

Checklist

  • I have read the CONTRIBUTING document
  • I have reviewed and understand every line of code in this PR, including any generated by AI tools, and I can explain why it works
  • My change is focused and reasonably small; I have split unrelated work into separate PRs
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

When response.incomplete arrives with reason max_output_tokens after a
function_call output item has started, tool_calls took precedence over
the length stop reason, so the event loop executed the truncated tool
call (with an empty input) instead of running max-tokens recovery.
Check the length stop reason first, matching OpenAIModel.

Fixes strands-agents#4135
@BlueX888
BlueX888 requested a review from a team as a code owner September 3, 2026 13:01
@BlueX888
BlueX888 requested a review from liramon2 September 3, 2026 13:01
@github-actions github-actions Bot added area-model Related to models or model providers bug Something isn't working python Pull requests that update python code labels Sep 3, 2026
@github-actions github-actions Bot added complexity/low Touched functions have low cognitive complexity (<=10) size/xs labels Sep 3, 2026
@codecov

codecov Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@liramon2
liramon2 merged commit 4bddde6 into strands-agents:main Sep 3, 2026
39 of 41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-model Related to models or model providers bug Something isn't working complexity/low Touched functions have low cognitive complexity (<=10) python Pull requests that update python code size/xs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] OpenAIResponsesModel reports tool_use instead of max_tokens when a function call is cut off by max_output_tokens

2 participants