Handle incomplete tool-call history during replay#923
Conversation
There was a problem hiding this comment.
Code Review
This pull request replaces strict runtime exceptions with warning logs and graceful handling when encountering incomplete tool-call turns or orphan tool results during memory replay. A critical bug was identified in the new _append_complete_tool_results helper: if some tool results are appended to messages before a missing one is detected, the assistant message will not be correctly popped in _drop_incomplete_tool_call_turn. It is recommended to validate that all expected tool results are present before appending any of them.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def _append_complete_tool_results(): | ||
| nonlocal tool_calls_map, last_tool_calls | ||
| for tool_call_id in last_tool_calls: | ||
| if tool_call_id not in tool_calls_map: | ||
| _drop_incomplete_tool_call_turn(f"missing tool result for {tool_call_id}") | ||
| return | ||
| messages.append(tool_calls_map.get(tool_call_id)) | ||
| matched_tool_call_ids.add(tool_call_id) | ||
| tool_calls_map = {} | ||
| last_tool_calls = [] |
There was a problem hiding this comment.
In _append_complete_tool_results, if a tool call is missing its result, _drop_incomplete_tool_call_turn is called. However, because the loop appends tool results to messages as it iterates, some tool results may have already been appended to messages before the missing one is encountered. At that point, messages[-1] will be a tool message (role: 'tool') rather than the assistant message. Consequently, the condition messages[-1].get("role") == "assistant" in _drop_incomplete_tool_call_turn will evaluate to False, and the assistant message will not be popped/dropped, leaving an incomplete tool-call turn in the replayed history.
To fix this, we should verify that all expected tool results are present in tool_calls_map before appending any of them to messages.
| def _append_complete_tool_results(): | |
| nonlocal tool_calls_map, last_tool_calls | |
| for tool_call_id in last_tool_calls: | |
| if tool_call_id not in tool_calls_map: | |
| _drop_incomplete_tool_call_turn(f"missing tool result for {tool_call_id}") | |
| return | |
| messages.append(tool_calls_map.get(tool_call_id)) | |
| matched_tool_call_ids.add(tool_call_id) | |
| tool_calls_map = {} | |
| last_tool_calls = [] | |
| def _append_complete_tool_results(): | |
| nonlocal tool_calls_map, last_tool_calls | |
| for tool_call_id in last_tool_calls: | |
| if tool_call_id not in tool_calls_map: | |
| _drop_incomplete_tool_call_turn(f"missing tool result for {tool_call_id}") | |
| return | |
| for tool_call_id in last_tool_calls: | |
| messages.append(tool_calls_map.get(tool_call_id)) | |
| matched_tool_call_ids.add(tool_call_id) | |
| tool_calls_map = {} | |
| last_tool_calls = [] |
Handle incomplete tool-call history during replay