C.W.K.
Stream
Lesson 04 of 08 · published

Handling Disconnections

~11 min · fastapi, disconnect, cleanup

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

WebSocketDisconnect is your tell

When the client closes — clean or abrupt — the next receive_* call raises WebSocketDisconnect. The exception carries .code (the close code) and .reason (the close frame's reason). This is your single authoritative signal that the session is ending.

Cleanup belongs in the except block

Whatever you set up on connect (added to a connection manager, registered a heartbeat task, opened a database row) must be torn down here. The pattern is connect → register → try/loop/except → unregister. Do not skip the unregister; orphaned references are how WebSocket servers leak memory at scale.

Other exceptions still happen

Application bugs in your message handler will throw their own exceptions. Catch them separately so you can return a structured error to the client and still close cleanly.

Code

The full connect/cleanup pattern·python
from fastapi import WebSocket, WebSocketDisconnect

@app.websocket('/ws')
async def chat(websocket: WebSocket):
    await websocket.accept()
    user_id = await register(websocket)  # your manager
    try:
        async for msg in websocket.iter_json():
            await handle(websocket, user_id, msg)
    except WebSocketDisconnect as e:
        log.info('client gone: code=%s reason=%s', e.code, e.reason)
    except Exception as e:
        log.exception('handler error')
        try:
            await websocket.close(code=1011, reason='internal error')
        except Exception:
            pass  # already closed
    finally:
        await unregister(user_id)
Reading e.code to react·python
except WebSocketDisconnect as e:
    if e.code == 1000:
        log.info('clean close')
    elif e.code == 1001:
        log.info('client navigated away')
    elif e.code == 1006:
        log.warning('abnormal close — laptop lid or NAT timeout, probably')
    else:
        log.warning('disconnect %s: %s', e.code, e.reason)

External links

Exercise

Modify your echo server so it logs a counter (active connections: N) on every connect/disconnect. Force-quit your test client, kill its process, and close it cleanly — observe that the counter returns to 0 in all three cases. If it does not, your finally is wrong.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.