Skip to content

[Bugfix] Fix KeyError in parse_response_input for reasoning items with optional content#34499

Merged
DarkLight1337 merged 1 commit intovllm-project:mainfrom
jeonsworld:fix/responses-reasoning-optional-content
Mar 12, 2026
Merged

[Bugfix] Fix KeyError in parse_response_input for reasoning items with optional content#34499
DarkLight1337 merged 1 commit intovllm-project:mainfrom
jeonsworld:fix/responses-reasoning-optional-content

Conversation

@jeonsworld
Copy link
Contributor

@jeonsworld jeonsworld commented Feb 13, 2026

Purpose

Fix KeyError: 'content' crash in parse_response_input() when a reasoning input item omits the optional content field during multi-turn Responses API conversations.

Per the OpenAI spec, ResponseReasoningItem.content is Optional[List[Content]] = None. Clients like langchain-openai omit this field when constructing multi-turn input from previous responses, causing:

# harmony_utils.py:224-227 (before)
content = response_msg["content"]    # KeyError if key is absent
assert len(content) == 1             # AssertionError if content is None or []

This fix uses .get("content") with fallback to summary text, matching the existing defensive pattern already used in utils.py:190-201.

Fixes #34496
Related: #33089

Test Plan

pytest tests/entrypoints/openai/parser/test_harmony_utils.py::TestParseResponseInputReasoningItem -v

4 unit tests added:

Test Scenario
test_reasoning_with_content content present (existing behavior preserved)
test_reasoning_without_content_uses_summary content key omitted, fallback to summary
test_reasoning_with_none_content_uses_summary content=None, fallback to summary
test_reasoning_without_content_or_summary neither content nor summary, empty string fallback

Test Result

Input format Before After
content key omitted 500 KeyError OK (uses summary)
"content": null 500 TypeError OK (uses summary)
"content": [{"type": "reasoning_text", "text": "..."}] OK OK (unchanged)

Essential Elements of an Effective PR Description Checklist
  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan, such as providing test command.
  • The test results, such as pasting the results comparison before and after, or e2e results
  • (Optional) The necessary documentation update, such as updating supported_models.md and examples for a new model.
  • (Optional) Release notes update. If your change is user facing, please update the release notes draft in the Google Doc.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request effectively addresses the KeyError in parse_response_input by implementing a more robust content retrieval mechanism for reasoning items. The new tests cover various scenarios, ensuring the fix handles cases with and without content, and with None content, falling back to summary as expected. The changes improve the reliability of the parse_response_input function when dealing with optional fields in the OpenAI spec.

parse_chat_output,
parse_input_to_harmony_message,
parse_output_message,
parse_response_input,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The parse_response_input function is imported but not used in the TestCommonParseInputToHarmonyMessage class, which tests functions common to both Chat Completion and Responses API. This import is only relevant for TestParseResponseInputReasoningItem.

Suggested change
parse_response_input,
parse_chat_output,
parse_input_to_harmony_message,
parse_output_message,
)

@mergify
Copy link

mergify bot commented Feb 13, 2026

Hi @jeonsworld, the pre-commit checks have failed. Please run:

uv pip install pre-commit
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy or markdownlint failing?
mypy and markdownlint are run differently in CI. If the failure is related to either of these checks, please use the following commands to run them locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10
# For markdownlint
pre-commit run --hook-stage manual markdownlint

@jeonsworld jeonsworld force-pushed the fix/responses-reasoning-optional-content branch from 340245c to 53c7cb7 Compare February 13, 2026 10:22
@abhiram1809
Copy link

LGTM

Copy link
Contributor

@bbrowning bbrowning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! I'm not a maintainer for this area, but am very familiar with conversion of things to/from the Harmony format and fixed a lot of issues here on the Chat Completions side, and would like to request a few changes to better match the Harmony format conversion for these items.

First, I don't think we should fall back to the summary content here. That summary is not actually something generated directly by the model in its Harmony output, and not something vLLM ever creates. This field is more typically generated by SaaS services as a separate step to provide user-safe reasoning summaries since the raw Chain of Thought is not meant to be shown to the user.

Second, the reasoning items can actually be more than 1 here. Since you're now not asserting they are exactly one, we should use all of them found and not only the first. It's probably reasonable for now to just concatenate the reasoning items together in a string into a single Harmony Message, since this method expects to return a single Message from the single ResponseInputOutputItem.

Third, we need to set the Harmony channel (using msg.with_channel) to analysis since these were originally output to that analysis channel in the original response.

Lastly, if this is actually empty, we should not append an analysis message at all. That means we may need to modify this method to allow returning None and adjust the caller in vllm/entrypoints/openai/responses/serving.py to not append this if it returns None. Or, we could optionally modify this to return a list of Message items (instead of a single), so that we could return an empty list or return a list with multiple (for the case of more than 1 reasoning item) and then adjust the caller to append all items from this list to its message stack instead of appending a single.

If you aren't comfortable making these changes, then I can instead turn this into a separate issue so we can knock these items out in a different PR. But, since you're in here and making this better, I'm pointing these out.

Thanks!

@github-project-automation github-project-automation bot moved this from To Triage to In progress in gpt-oss Issues & Enhancements Feb 25, 2026
@jeonsworld jeonsworld force-pushed the fix/responses-reasoning-optional-content branch from 53c7cb7 to 31c085e Compare February 27, 2026 08:53
@mergify
Copy link

mergify bot commented Feb 27, 2026

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @jeonsworld.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify bot added the needs-rebase label Feb 27, 2026
@jeonsworld jeonsworld force-pushed the fix/responses-reasoning-optional-content branch from 31c085e to 67e53d9 Compare February 27, 2026 08:57
@mergify mergify bot removed the needs-rebase label Feb 27, 2026
@jeonsworld
Copy link
Contributor Author

Thanks for the detailed review! I've addressed all your feedback @bbrowning :

  • Removed the summary fallback
  • Now concatenating all content items instead of using only the first
  • Set the channel to analysis
  • Returning None for empty/missing content and updated the caller to skip it

@mergify
Copy link

mergify bot commented Feb 27, 2026

Hi @jeonsworld, the pre-commit checks have failed. Please run:

uv pip install pre-commit
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Tip

Is mypy or markdownlint failing?
mypy and markdownlint are run differently in CI. If the failure is related to either of these checks, please use the following commands to run them locally:
# For mypy (substitute "3.10" with the failing version if needed)
pre-commit run --hook-stage manual mypy-3.10
# For markdownlint
pre-commit run --hook-stage manual markdownlint

…h optional content

Per the OpenAI spec, ResponseReasoningItem.content is
Optional[List[Content]] = None. Clients like langchain-openai may omit
this field when constructing multi-turn input from previous responses,
causing a KeyError.

Changes based on reviewer feedback:
- Remove summary fallback (SaaS-generated, not produced by vLLM)
- Concatenate all reasoning content items instead of using only the first
- Set Harmony channel to 'analysis' for reasoning messages
- Return None for empty/missing content instead of an empty message
- Update caller in serving.py to skip None results

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: jeonsworld <jeonsworld@gmail.com>
@jeonsworld jeonsworld force-pushed the fix/responses-reasoning-optional-content branch from 67e53d9 to f5f8c04 Compare February 27, 2026 09:07
@jeonsworld jeonsworld requested a review from bbrowning February 27, 2026 09:09
Copy link
Contributor

@bbrowning bbrowning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates - the changes and tests look good to me from a Harmony correctness point-of-view. This approval clears my suggested changes status, but this will still need approval from a maintainer to get merged.

To approvers, this fixes a compatibility issue in multi-turn interactions in opencode and codex when it comes to clients passing reasoning information back in. And, it fixes a less obvious correctness/accuracy issue where even when reasoning was getting passed back in it wasn't being properly set to the analysis channel, resulting in us prompting the model suboptimally in multi-turn reasoning scenarios with Harmony models.

@mgoin mgoin added the ready ONLY add when PR is ready to merge/full CI is needed label Feb 27, 2026
@qandrew
Copy link
Contributor

qandrew commented Mar 10, 2026

hi @mgoin should we merge this in?

@bbrowning
Copy link
Contributor

+1 to merging this - it fixes an important compatibility issues here and there are other subsequent PRs also attempting to fix the same thing.

@github-project-automation github-project-automation bot moved this from In progress to Ready in gpt-oss Issues & Enhancements Mar 12, 2026
@DarkLight1337 DarkLight1337 merged commit bdc2343 into vllm-project:main Mar 12, 2026
51 checks passed
athrael-soju pushed a commit to athrael-soju/vllm that referenced this pull request Mar 16, 2026
…h optional content (vllm-project#34499)

Signed-off-by: jeonsworld <jeonsworld@gmail.com>
Signed-off-by: Athrael Soju <athrael.soju@gmail.com>
Lucaskabela pushed a commit to Lucaskabela/vllm that referenced this pull request Mar 17, 2026
…h optional content (vllm-project#34499)

Signed-off-by: jeonsworld <jeonsworld@gmail.com>
wendyliu235 pushed a commit to wendyliu235/vllm-public that referenced this pull request Mar 18, 2026
…h optional content (vllm-project#34499)

Signed-off-by: jeonsworld <jeonsworld@gmail.com>
fxdawnn pushed a commit to fxdawnn/vllm that referenced this pull request Mar 19, 2026
…h optional content (vllm-project#34499)

Signed-off-by: jeonsworld <jeonsworld@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working frontend gpt-oss Related to GPT-OSS models ready ONLY add when PR is ready to merge/full CI is needed

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: Responses API crashes with KeyError when reasoning input item has no content field

6 participants