🐛 Bug: Long Conversation Triggers "tool message without toolcalls" Error
Description
When a conversation exceeds a certain length threshold, it triggers an LLM API error:
Messages with role 'tool' must be a response to a preceding message with 'toolcalls'
Starting a new conversation fixes the issue, confirming it's related to message history length.
Steps to Reproduce
- Have a conversation with multiple tool/function calls
- Continue the conversation past a certain message count or token threshold
- Attempt another tool call
- Error:
invalidrequesterror - tool message without preceding toolcalls
Root Cause Analysis
The issue is likely caused by message history truncation in one of these scenarios:
Scenario 1: Truncation cuts at tool_calls boundary
[正常历史]
Assistant: has tool_calls: [search]
User (tool): "search results..."
Assistant: has tool_calls: [write_file]
User (tool): "file written..."
[截断后]
User (tool): "file written..." ← 没有前面的 tool_calls,API 报错!
Scenario 2: Inconsistent message pairing
The message history has a role: 'tool' message that doesn't correspond to any preceding tool_calls message, possibly due to:
- Incomplete truncation logic
- Race condition in message processing
- Message deduplication removing the tool_calls parent
Suggested Fix
Option 1: Smarter Truncation
# Before truncating, ensure we don't cut in the middle of a tool/tool_calls pair
def truncate_history(messages, max_tokens):
# Find safe truncation points (before tool_calls messages)
safe_points = [i for i, m in enumerate(messages)
if m.get('role') == 'assistant' and 'tool_calls' in m]
# Truncate before the oldest safe point that leaves room
...
Option 2: Validate Message Chain Before API Call
def validate_message_chain(messages):
tool_calls_seen = set()
for msg in messages:
if msg.get('role') == 'tool':
# Check if this tool message has a valid parent
if msg.get('tool_call_id') not in tool_calls_seen:
raise ValueError("Tool message without preceding tool_calls")
if 'tool_calls' in msg:
for tc in msg['tool_calls']:
tool_calls_seen.add(tc.get('id'))
Option 3: Automatic Repair
def repair_message_history(messages):
# Remove orphan tool messages
valid_tool_ids = set()
for msg in messages:
if 'tool_calls' in msg:
for tc in msg['tool_calls']:
valid_tool_ids.add(tc.get('id'))
return [m for m in messages
if m.get('role') != 'tool' or m.get('tool_call_id') in valid_tool_ids]
Affected Components
- LLM caller / message history management
- Conversation truncation logic
Labels
- bug
- llm
- conversation-management
Priority
High - This blocks users from having longer conversations
🐛 Bug: Long Conversation Triggers "tool message without toolcalls" Error
Description
When a conversation exceeds a certain length threshold, it triggers an LLM API error:
Starting a new conversation fixes the issue, confirming it's related to message history length.
Steps to Reproduce
invalidrequesterror- tool message without preceding toolcallsRoot Cause Analysis
The issue is likely caused by message history truncation in one of these scenarios:
Scenario 1: Truncation cuts at tool_calls boundary
Scenario 2: Inconsistent message pairing
The message history has a
role: 'tool'message that doesn't correspond to any precedingtool_callsmessage, possibly due to:Suggested Fix
Option 1: Smarter Truncation
Option 2: Validate Message Chain Before API Call
Option 3: Automatic Repair
Affected Components
Labels
Priority
High - This blocks users from having longer conversations