fix(ui): prevent WebSocket reconnection loop #214
Conversation
PR #213 stabilised fetchAgents by moving URL params into refs and running the effect only on mount. A side-effect was that navigating to a conversation URL (e.g. from a Discord link) could trigger an infinite WebSocket reconnection loop when the conversation was not immediately available in the list response. Root causes and fixes: 1. fetchAgents auto-created a second conversation when the URL-specified one was missing from the list. Now it tries a direct GET for the conversation first, and if that also fails it returns early instead of auto-creating a duplicate. 2. applyConversationUpdate silently dropped WebSocket events when selectedConversation was null (null && ... evaluates to null). Changed the guard from (prev && prev.name === conv.name) to (!prev || prev.name === conv.name) so the first event properly sets the selection. 3. After mount, URL param changes had no way to update the selected conversation from already-fetched data. Added a useEffect that watches [name, urlConversationId, agents] and syncs the selection.
📋 GitRank PR AnalysisScore: 0 points (ineligible)
Eligibility Checks
Impact SummaryThis PR fixes a critical bug introduced in PR #213 where WebSocket connections enter an infinite reconnection loop when navigating to Discord-spawned agent conversations. The fix addresses three root causes: preventing duplicate conversation auto-creation, fixing null-guard logic in WebSocket event handling, and adding URL parameter change detection. The changes restore proper WebSocket connectivity and conversation selection without breaking existing functionality (all 202 tests pass). Analysis DetailsComponent Classification: This is a UI bug fix affecting WebSocket connection logic in the chat page component. While it's UI-focused, it doesn't fit neatly into a specific feature category, making OTHER the appropriate classification. Severity Justification: This is a High (P1) severity issue because it causes a critical functional failure—an infinite WebSocket reconnection loop that breaks the application's core real-time messaging capability. Users cannot use the chat feature when spawning agents via Discord, representing major impact on core functionality. Eligibility Notes: Issue: True—PR clearly describes a bug (infinite reconnection loop) introduced by a prior merge. Fix Implementation: True—code changes directly address the three identified root causes with appropriate logic fixes. PR Linked: True—comprehensive description with root cause analysis and test plan. Tests: False—no test files were modified or added. Tests Required: True—this is a bug fix in business logic (WebSocket connection handling and state management) that affects core functionality, requiring test coverage to prevent regression. Analyzed by GitRank 🤖 |
After merging #213, spawning an agent through Discord and navigating to its conversation link causes an infinite WebSocket reconnection loop with two different conversation IDs alternating in a tight React reconciliation cycle.
Three root causes combined to break WebSocket connections:
fetchAgents only runs on mount now (useEffect with [] deps). When the URL-specified conversation wasn't in the list response, it fell through and auto-created a second conversation. Two conversations meant selectedConversation alternated between them, causing conversationId to flip on every render, which tore down and recreated the WebSocket in a tight loop.
Fix: Before auto-creating, try a direct GET /acp/conversations/{id} for the URL-specified conversation. If found, select it. If not found, return early instead of creating a duplicate. This eliminates the dual-conversation scenario entirely.
applyConversationUpdate had a null guard that silently dropped all WebSocket events when selectedConversation was null. The check (prev && prev.metadata.name === conversation.metadata.name) evaluates to null when prev is null, so the first WebSocket event could never set the selection.
Fix: Changed to (!prev || prev.metadata.name === conversation.metadata.name) so updates are accepted when there is no current selection.
After mount, URL param changes (e.g. navigating from a Discord link or switching conversations) had no way to update the selected conversation from already-fetched agent data since fetchAgents no longer re-runs on URL changes.
Fix: Added a useEffect watching [name, urlConversationId, agents] that looks up and selects the matching conversation from existing state.
The sidebar alphabetical sorting and catch-all route from #213 are untouched. All 202 existing tests pass.
Test plan