CoplayDev/unity-mcp · error · ConnectionError

Connection closed before reading expected bytes

Error message

Connection closed before reading expected bytes

What it means

Raised by _read_exact (unity_connection.py:168) in the framed receive path. It loops calling sock.recv() to assemble a fixed byte count (the 8-byte length header or a payload); an empty recv() means TCP EOF before the expected bytes arrived, so the peer closed mid-message.

Source

Thrown at Server/src/transport/legacy/unity_connection.py:168

        except BlockingIOError:
            pass  # No data pending; socket is alive
        except Exception:
            logger.debug("Stale socket detected; will reconnect on next send")
            try:
                self.sock.close()
            except Exception:
                pass
            self.sock = None
        finally:
            if self.sock and orig_blocking is not None:
                self.sock.setblocking(orig_blocking)

    def _read_exact(self, sock: socket.socket, count: int) -> bytes:
        data = bytearray()
        while len(data) < count:
            chunk = sock.recv(count - len(data))
            if not chunk:
                raise ConnectionError(
                    "Connection closed before reading expected bytes")
            data.extend(chunk)
        return bytes(data)

    def receive_full_response(self, sock, buffer_size=config.buffer_size) -> bytes:
        """Receive a complete response from Unity, handling chunked data."""
        if self.use_framing:
            # Heartbeat semantics: the Unity editor emits zero-length frames while
            # a long-running command is still executing. We tolerate a bounded
            # number of these frames (or a small time window) before surfacing a
            # timeout to the caller so tools can retry or fail gracefully.
            heartbeat_limit = getattr(config, 'max_heartbeat_frames', 16)
            heartbeat_window = getattr(config, 'heartbeat_timeout', 2.0)
            heartbeat_started = time.monotonic()
            heartbeat_count = 0
            try:
                while True:
                    header = self._read_exact(sock, 8)

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Let send_command's retry loop handle it — it catches the failure, drops the dead socket, and reconnects on the next attempt.
  2. Keep Unity open and avoid forcing recompiles during long tool calls.
  3. If it recurs constantly, inspect the Unity Editor console for crashes or repeated domain reloads.

Example fix

// before
resp = conn.send_command(cmd, params)  # raises on EOF

// after — rely on retry; only surface to the user after retries are exhausted
resp = conn.send_command(cmd, params, max_attempts=config.max_retries)
Defensive patterns

Strategy: retry

Validate before calling

# Validate the socket is live before sending
import select

def socket_alive(sock) -> bool:
    if sock is None:
        return False
    r, _, x = select.select([sock], [], [sock], 0)
    if x or (r and not sock.recv(1, socket.MSG_PEEK)):
        return False  # EOF or error pending
    return True

Type guard

def is_mid_stream_eof(e: BaseException) -> bool:
    return (isinstance(e, ConnectionError)
            and 'Connection closed before reading expected bytes' in str(e))

Try / catch

# Rely on send_command's built-in retry; it drops the dead socket and reconnects.
try:
    resp = conn.send_command(cmd, params, max_attempts=config.max_retries)
except ConnectionError as e:
    if 'Connection closed before reading expected bytes' in str(e):
        conn.disconnect()
        resp = conn.send_command(cmd, params)  # one manual reconnect+retry

Prevention

When it happens

Trigger: receive_full_response in framing mode reading the 8-byte header or the payload when Unity closes the socket — e.g. a Unity domain reload, an editor crash, or the user quitting Unity while a command is in flight.

Common situations: Unity recompiling scripts (domain reload invalidates the socket), the editor being closed mid-operation, or a network device killing the connection during a large framed response.

Understand the failure class

Related errors


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/4c266954eee3b46a. Report an issue: GitHub.