CoplayDev/unity-mcp · warning · ConnectionError
Connection closed while reading
Error message
Connection closed while reading
What it means
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.
Source
Thrown at tools/stress_editor_state.py:57
default_port = 6400
files = find_status_files()
for f in files:
try:
data = json.loads(f.read_text())
port = int(data.get("unity_port", 0) or 0)
if 0 < port < 65536:
return port
except Exception:
pass
return default_port
async def read_exact(reader: asyncio.StreamReader, n: int) -> bytes:
buf = b""
while len(buf) < n:
chunk = await reader.read(n - len(buf))
if not chunk:
raise ConnectionError("Connection closed while reading")
buf += chunk
return buf
async def read_frame(reader: asyncio.StreamReader) -> bytes:
header = await read_exact(reader, 8)
(length,) = struct.unpack(">Q", header)
if length <= 0 or length > (64 * 1024 * 1024):
raise ValueError(f"Invalid frame length: {length}")
return await read_exact(reader, length)
async def write_frame(writer: asyncio.StreamWriter, payload: bytes) -> None:
header = struct.pack(">Q", len(payload))
writer.write(header)
writer.write(payload)
await asyncio.wait_for(writer.drain(), timeout=TIMEOUT)
View on GitHub (pinned to c21bf496bc)
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.
Defensive patterns
Strategy: retry
Validate before calling
import socket
def bridge_reachable(host: str, port: int, timeout: float = 2.0) -> bool:
try:
with socket.create_connection((host, port), timeout=timeout):
return True
except OSError:
return False
# Before starting the stress loop:
if not bridge_reachable(host, port):
raise SystemExit(f"Bridge not reachable at {host}:{port}") Try / catch
# The stress_loop already does this:
try:
response = await asyncio.wait_for(read_frame(reader), timeout=TIMEOUT)
except (ConnectionError, OSError, asyncio.TimeoutError) as e:
stats["errors"] += 1
stats["reconnects"] += 1
writer = None # force reconnect on next iteration
await asyncio.sleep(0.5) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
Related errors
- Connection closed while reading
- Invalid frame length: {length}
- Unexpected handshake from server: {line!r}
- Invalid frame length: {length}
- Unexpected handshake from server: {line!r}
AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13).
Data as JSON: /api/errors/fc05ba83de7ae778.
Report an issue: GitHub.