From 8a52e3dfa3b953e83130c983e35f4cf87615447b Mon Sep 17 00:00:00 2001 From: Jeuners Date: Mon, 15 Jun 2026 02:04:49 +0200 Subject: [PATCH] Fix WebSocket disconnect crash and missing 'personality' field Two bugs that crashed the engine in production: 1. world.nearby_agents() did not SELECT the 'personality' column, so _reaction_turn raised KeyError on every reactive trigger, killing the engine thread silently. Engine-Thread stieg aus ohne Log. Fix: select personality and json-parse it so callers get a real list, matching agents_mod.get(). 2. server.py ws() handler caught the generic 'Exception' from asyncio.to_thread(queue.get) and tried to send a ping back, but the WebSocket was already closed by the client. Starlette raised RuntimeError: Cannot call 'send' once a close message has been sent. Fix: drop the ping, just break the loop on any exception. Client disconnect now handled cleanly. Live-verified: 0 errors in log after 3 abrupt disconnects, engine continues producing ticks. --- engine/world.py | 9 ++++++++- server.py | 6 ++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/engine/world.py b/engine/world.py index c1358ca..df88459 100644 --- a/engine/world.py +++ b/engine/world.py @@ -95,7 +95,8 @@ def nearby_agents(agent_id: str, x: int, y: int, radius: float = HEARING_DISTANC c.row_factory = sqlite3.Row try: rows = c.execute( - "SELECT id,name,x,y,energy FROM agents WHERE id!=? AND alive=1", (agent_id,) + "SELECT id,name,personality,x,y,energy FROM agents " + "WHERE id!=? AND alive=1", (agent_id,) ).fetchall() out = [] for r in rows: @@ -103,6 +104,12 @@ def nearby_agents(agent_id: str, x: int, y: int, radius: float = HEARING_DISTANC if d <= radius: d2 = dict(r) d2["distance"] = d + # personality is stored as a JSON string; parse so callers + # get a real list (matches agents_mod.get). + try: + d2["personality"] = json.loads(d2.get("personality") or "[]") + except Exception: + d2["personality"] = [] out.append(d2) return out finally: diff --git a/server.py b/server.py index eb441a8..6437784 100644 --- a/server.py +++ b/server.py @@ -164,8 +164,10 @@ async def ws(ws: WebSocket): try: msg = await asyncio.to_thread(queue.get, timeout=30.0) await ws.send_json(msg) + except WebSocketDisconnect: + break except Exception: - # heartbeat - await ws.send_json({"type": "ping", "ts": time.time()}) + # client went away — stop sending + break except WebSocketDisconnect: pass