fix(run): copy raw_responses when building a RunState - #4237
Merged
Conversation
to_state() assigned result.raw_responses directly to the new state, so resuming from that state appended the next turn's responses onto the list still held by the RunResult that was already returned to the caller. The sibling assignment one line above copies with list(result.new_items), and the resume block in run.py copies session_items and the guardrail result lists out of the run state the same way, so this one was the outlier.
seratch
approved these changes
Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
to_state()assignsresult.raw_responsesstraight onto the newRunState, so the state and the already-returnedRunResultshare one list object. Resuming that state throughRunner.run()appends the next turn'sModelResponseonto that shared list, which silently grows theraw_responsesof aRunResultthe caller was handed earlier. The assignment one line above it in the same function copies withlist(result.new_items), and the resume block inrun.pycopiessession_itemsand the three guardrail result lists out of the run state the same way, so this line was the lone outlier. The fix copies the list.This is distinct from #3924, which was closed because it changed the
_model_input_itemsfallback branch that is unreachable for SDK-created results. This line is not branch guarded and reproduces through the plain public API. On main the new test fails withassert 2 == 1, becauseresult1.raw_responsesgrows from one response to two after a secondRunner.run()resumes the state. The streamed path is unaffected becauserun_loop.pyrebindsstreamed_result.raw_responsesto a newly concatenated list rather than appending in place, so only theRunner.run()resume path shows the bug.make lint,make typecheck, andmake testsall pass.