Skip to content

Fix infinite loop in fix_message_list for out-of-order tool messages causing indefinite hang#411

Open
cryansky wants to merge 2 commits intoaliasrobotics:mainfrom
cryansky:fix/indefinite-hang-in-fix_message_list
Open

Fix infinite loop in fix_message_list for out-of-order tool messages causing indefinite hang#411
cryansky wants to merge 2 commits intoaliasrobotics:mainfrom
cryansky:fix/indefinite-hang-in-fix_message_list

Conversation

@cryansky
Copy link

Fix #410

Summary

  • Fix infinite loop in fix_message_list caused by an overly strict is_valid_sequence check that only considered the immediate predecessor, not the nearest preceding assistant message.
  • Add some tests covering reversed, scrambled, nested, and multi-assistant tool response ordering.

Context

The is_valid_sequence check in the second pass of fix_message_list only looked at the immediate predecessor of a tool message:

is_valid_sequence = (
    prev_msg.get("role") == "assistant"
    and prev_msg.get("tool_calls")
    and any(tc.get("id") == tool_id for tc in prev_msg.get("tool_calls", []))
)

This is correct for single-tool-call assistants (assistant → tool), but fails for multi-tool-call assistants where the valid sequence is assistant → (agent-as-a-)tool → (agent-as-a-)tool. The second tool message's predecessor is the first tool message, not the assistant, so it's incorrectly flagged as out-of-sequence.

Because of that, the reorder logic pops the tool message and inserts it right after the assistant. But this displaces the other tool message to the same index, which then also fails the same check, gets moved, displaces the first one back, and so on. This creates an infinite swap loop at a fixed index i that never increments.

Fix

Replace the single-predecessor check with a backward walk that skips past other tool messages to find the nearest assistant message. If that assistant contains the matching tool_call_id, the sequence is valid and no reordering is needed.

is_valid_sequence = False
for k in range(i - 1, -1, -1):
    prev = processed_messages[k]
    if prev.get("role") == "assistant" and prev.get("tool_calls"):
        if any(tc.get("id") == tool_id for tc in prev.get("tool_calls", [])):
            is_valid_sequence = True
        break
    elif prev.get("role") == "tool":
        continue
    else:
        break

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Infinite Loop Bug in fix_message_list Function when using Nested Agent-as-a-tool caused CAI to Hang Indefinitely

1 participant