{"record":{"id":"fc05ba83de7ae778","repo":"CoplayDev/unity-mcp","slug":"connection-closed-while-reading","errorCode":null,"errorMessage":"Connection closed while reading","messagePattern":"Connection closed while reading","errorType":"exception","errorClass":"ConnectionError","httpStatus":null,"severity":"warning","filePath":"tools/stress_editor_state.py","lineNumber":57,"sourceCode":"    default_port = 6400\n    files = find_status_files()\n    for f in files:\n        try:\n            data = json.loads(f.read_text())\n            port = int(data.get(\"unity_port\", 0) or 0)\n            if 0 < port < 65536:\n                return port\n        except Exception:\n            pass\n    return default_port\n\n\nasync def read_exact(reader: asyncio.StreamReader, n: int) -> bytes:\n    buf = b\"\"\n    while len(buf) < n:\n        chunk = await reader.read(n - len(buf))\n        if not chunk:\n            raise ConnectionError(\"Connection closed while reading\")\n        buf += chunk\n    return buf\n\n\nasync def read_frame(reader: asyncio.StreamReader) -> bytes:\n    header = await read_exact(reader, 8)\n    (length,) = struct.unpack(\">Q\", header)\n    if length <= 0 or length > (64 * 1024 * 1024):\n        raise ValueError(f\"Invalid frame length: {length}\")\n    return await read_exact(reader, length)\n\n\nasync def write_frame(writer: asyncio.StreamWriter, payload: bytes) -> None:\n    header = struct.pack(\">Q\", len(payload))\n    writer.write(header)\n    writer.write(payload)\n    await asyncio.wait_for(writer.drain(), timeout=TIMEOUT)\n","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/tools/stress_editor_state.py#L39-L75","documentation":"Raised by read_exact() in stress_editor_state.py when asyncio.StreamReader.read() returns an empty bytes object before the requested byte count is satisfied. An empty read signals the remote end (the Unity MCP TCP bridge) closed the socket. This is a normal occurrence under stress — the bridge may drop idle or misbehaving connections, restart during domain reload, or hit a connection cap.","triggerScenarios":"The Unity Editor bridge closed the TCP connection mid-frame: the editor exited, recompiled (domain reload tears down the bridge), hit a max-connection limit, or the OS killed the socket. Also fires if the bridge was never fully started and accepts then immediately drops the connection.","commonSituations":"Running the stress test while Unity is recompiling scripts (domain reload restarts the bridge); the bridge has a connection ceiling and the stress tool exceeds it; the editor crashed or was closed mid-test; network/firewall interference on the loopback socket.","solutions":["This is expected behavior under stress — the stress_loop already catches ConnectionError and reconnects. Increase reconnect delay or reduce request rate if errors dominate.","Ensure Unity is running and the bridge is active before starting: check ~/.unity-mcp/unity-mcp-status-*.json for a valid unity_port.","If errors are constant (never a successful request), verify the port is correct and the bridge is listening.","Avoid running during Unity recompilation, or expect reconnect bursts around domain reloads."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import socket\n\ndef bridge_reachable(host: str, port: int, timeout: float = 2.0) -> bool:\n    try:\n        with socket.create_connection((host, port), timeout=timeout):\n            return True\n    except OSError:\n        return False\n\n# Before starting the stress loop:\nif not bridge_reachable(host, port):\n    raise SystemExit(f\"Bridge not reachable at {host}:{port}\")","typeGuard":null,"tryCatchPattern":"# The stress_loop already does this:\ntry:\n    response = await asyncio.wait_for(read_frame(reader), timeout=TIMEOUT)\nexcept (ConnectionError, OSError, asyncio.TimeoutError) as e:\n    stats[\"errors\"] += 1\n    stats[\"reconnects\"] += 1\n    writer = None  # force reconnect on next iteration\n    await asyncio.sleep(0.5)","preventionTips":["Treat ConnectionError as expected under stress — the loop reconnects automatically.","Verify the bridge is up and the port is correct before starting.","Reduce request intensity if errors outnumber successful requests."],"tags":["stress-test","network","tcp","unity-bridge","connection"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}