Duplicate assistant responses caused by stale/reconnected WebSocket**
Description
Some developers see the AI assistant (Altinity) respond twice with identical content to the same request — same commit hash, same file list, same text, rendered as two separate assistant bubbles in the chat. Other developers running the same code do not see the issue, so it is not deterministic / not reproducible for everyone.
Observed in
PR Altinn/altinn-studio#19661 (refactor/agentic-loop — "replace the fixed agent pipeline with an agentic tool-use loop"), discovered while locally testing a simple "add an input field" request.
Root cause (suspected, not 100% confirmed)
A situation that causes the problem is Multiple tabs/windows open simultaneously against Studio Designer with the same login. Since messages are broadcast per developer (to your entire SignalR group), all tabs you have open will receive the same assistant_message event. If more than one tab tries to persist/display it, you get duplicates.
The duplication does not come from the message-generation code in the new agent loop itself — that has been verified to send the final assistant_message event exactly once per run:
- agentic_loop_node.py → _emit_workflow_completion() sends only once, and the node runs only once per graph execution (runner.py).
The suspicion instead points to the connection-handling layer between browser and the Altinity backend, which is unchanged by this PR but is made easier to trigger by this new (longer-running, more "chatty") workflow:
- AltinityWebSocketService.cs maintains one persistent WebSocket to the Python agent service per developer, and broadcasts every message to a SignalR group named after that developer (_hubContext.Clients.Group(developer).ReceiveAgentMessage(...), AltinityWebSocketService.cs:179).
- On the frontend, useAltinityWebSocket.ts creates a brand-new WSConnector instance on every mount instead of using the singleton method the class actually provides (WSConnector.getInstance, see WSConnector.ts:19-24).
- The cleanup function in useAltinityWebSocket.ts only calls connection.off(...) (removing the local JS listener) — it never calls anything that actually stops/closes the underlying SignalR connection.
- Consequence: if the AI assistant page remounts while a workflow is running (page reload, switching tabs and back, HMR during dev, etc.), the developer can end up with two simultaneously live SignalR connections in their own developer group. Both receive the same ReceiveAgentMessage push, and if both still have an active listener attached, the assistant_message event gets handled twice → two identical, persisted assistant bubbles.
Since everything is scoped per developer (own WebSocket, own SignalR group, own event buffer), this is isolated to the individual developer's browser session — which explains why the issue doesn't affect everyone running the same code.
Reproduction steps (tentative — needs verification)
- Open multiple studio localhost pages and login to them.
- Ask a question to the assistant
- Wait for the response — check whether it appears twice.
Suggested fix
- Use WSConnector.getInstance(...) instead of new WSConnector(...) in useAltinityWebSocket.ts, so the connection is actually reused.
- Ensure the cleanup function actually terminates the SignalR connection (connection.stop()), not just .off().
- Consider adding a simple dedupe/idempotency check on the frontend (e.g. a message_id or trace_id per assistant_message event) as an extra safety net, so that even if the same event were delivered twice, it would only be rendered/persisted once.
Diagnostics to confirm
Check the studio_designer container logs for the line "Opened new agents WebSocket for developer ..." — if it appears twice for the same developer without a "Closed agents WebSocket" in between, that confirms two connections were live at the same time.
Affected files
- src/Designer/frontend/app-development/features/aiAssistant/hooks/useAltinityWebSocket/useAltinityWebSocket.ts
- src/Designer/frontend/packages/shared/src/websockets/WSConnector.ts
- src/Designer/backend/src/Designer/Services/Implementation/Altinity/AltinityWebSocketService.cs (context/understanding, likely not the actual fix location)
Duplicate assistant responses caused by stale/reconnected WebSocket**
Description
Some developers see the AI assistant (Altinity) respond twice with identical content to the same request — same commit hash, same file list, same text, rendered as two separate assistant bubbles in the chat. Other developers running the same code do not see the issue, so it is not deterministic / not reproducible for everyone.
Observed in
PR Altinn/altinn-studio#19661 (refactor/agentic-loop — "replace the fixed agent pipeline with an agentic tool-use loop"), discovered while locally testing a simple "add an input field" request.
Root cause (suspected, not 100% confirmed)
A situation that causes the problem is Multiple tabs/windows open simultaneously against Studio Designer with the same login. Since messages are broadcast per developer (to your entire SignalR group), all tabs you have open will receive the same assistant_message event. If more than one tab tries to persist/display it, you get duplicates.
The duplication does not come from the message-generation code in the new agent loop itself — that has been verified to send the final assistant_message event exactly once per run:
The suspicion instead points to the connection-handling layer between browser and the Altinity backend, which is unchanged by this PR but is made easier to trigger by this new (longer-running, more "chatty") workflow:
Since everything is scoped per developer (own WebSocket, own SignalR group, own event buffer), this is isolated to the individual developer's browser session — which explains why the issue doesn't affect everyone running the same code.
Reproduction steps (tentative — needs verification)
Suggested fix
Diagnostics to confirm
Check the studio_designer container logs for the line "Opened new agents WebSocket for developer ..." — if it appears twice for the same developer without a "Closed agents WebSocket" in between, that confirms two connections were live at the same time.
Affected files