Bug
When a client refreshes while the agent is blocked waiting for approval (e.g., bash tool), reconnection fails with "No active connection" error.
Reproduction
- Start a hosted agent with tool approval enabled
- Send a prompt that triggers a bash tool
- Agent sends
approval_needed, blocks on io.receive()
- Refresh the browser page
- Frontend shows "No active connection" error
- Agent logs:
✗ bash - connection closed
Root Cause
Three bugs compound:
1. run_agent() has no error handling
If the agent crashes (e.g., on io_closed sentinel), agent_finished.set() never fires. _pipe_ws_io hangs forever waiting.
File: connectonion/network/asgi/websocket.py — run_agent() function
2. Reattach uses closed IO
On reconnect, the server reattaches to the old io object which has _closed = True (set by io.close() on disconnect). io.send() silently drops all events — agent can't communicate with the new client.
File: connectonion/network/io/websocket.py — WebSocketIO._closed flag
3. Two _pipe_ws_io loops compete
The old loop (stuck waiting for agent_finished) and the new loop (from reattach) both reference the same agent_finished event, causing race conditions on completion.
File: connectonion/network/asgi/websocket.py — _pipe_ws_io()
Fix Plan
run_agent(): wrap in try/finally — always set agent_finished, capture error in error_holder
- Reattach: reopen IO — reset
io._closed = False so agent can send events through new WebSocket
- Old
_pipe_ws_io: detect superseded — when new connection reattaches, old pipe should exit cleanly
Documentation
Bug
When a client refreshes while the agent is blocked waiting for approval (e.g., bash tool), reconnection fails with "No active connection" error.
Reproduction
approval_needed, blocks onio.receive()✗ bash - connection closedRoot Cause
Three bugs compound:
1.
run_agent()has no error handlingIf the agent crashes (e.g., on
io_closedsentinel),agent_finished.set()never fires._pipe_ws_iohangs forever waiting.File:
connectonion/network/asgi/websocket.py—run_agent()function2. Reattach uses closed IO
On reconnect, the server reattaches to the old
ioobject which has_closed = True(set byio.close()on disconnect).io.send()silently drops all events — agent can't communicate with the new client.File:
connectonion/network/io/websocket.py—WebSocketIO._closedflag3. Two
_pipe_ws_ioloops competeThe old loop (stuck waiting for
agent_finished) and the new loop (from reattach) both reference the sameagent_finishedevent, causing race conditions on completion.File:
connectonion/network/asgi/websocket.py—_pipe_ws_io()Fix Plan
run_agent(): wrap in try/finally — always setagent_finished, capture error inerror_holderio._closed = Falseso agent can send events through new WebSocket_pipe_ws_io: detect superseded — when new connection reattaches, old pipe should exit cleanlyDocumentation
docs/network/session-reconnect.md(Known Issue section)docs/network/io.md,docs/network/session-websocket-lifecycle.md