CoplayDev/unity-mcp · warning · TimeoutError
Unity sent heartbeat frames without payload within configure
Error message
Unity sent heartbeat frames without payload within configured threshold
What it means
Raised in the framed receive loop (unity_connection.py:193). Unity emits zero-length frames as heartbeats while a long-running command is still executing. If only heartbeats arrive past max_heartbeat_frames (default 16) or heartbeat_timeout seconds (default 2.0s) without a real payload, the command is treated as wedged and a TimeoutError is raised.
Source
Thrown at Server/src/transport/legacy/unity_connection.py:193
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)
payload_len = struct.unpack('>Q', header)[0]
if payload_len == 0:
heartbeat_count += 1
logger.debug(
f"Received heartbeat frame #{heartbeat_count}")
if heartbeat_count >= heartbeat_limit or (time.monotonic() - heartbeat_started) > heartbeat_window:
raise TimeoutError(
"Unity sent heartbeat frames without payload within configured threshold"
)
continue
if payload_len > FRAMED_MAX:
raise ValueError(
f"Invalid framed length: {payload_len}")
payload = self._read_exact(sock, payload_len)
logger.debug(
f"Received framed response ({len(payload)} bytes)")
return payload
except socket.timeout as exc:
logger.warning("Socket timeout during framed receive")
raise TimeoutError("Timeout receiving Unity response") from exc
except TimeoutError:
raise
except Exception as exc:
logger.error(f"Error during framed receive: {exc}")
raiseView on GitHub (pinned to c21bf496bc)
Solutions
- Check Unity for a blocking modal dialog or a hung operation and dismiss/cancel it, then retry.
- If the operation is legitimately long, raise the budget via config.max_heartbeat_frames and/or config.heartbeat_timeout.
- Restart the Unity editor / recompile if a handler appears truly deadlocked.
Example fix
// before — default budget (16 frames / 2.0s) # config.max_heartbeat_frames = 16 // after — allow longer-running commands from core.config import config config.heartbeat_timeout = 10.0 config.max_heartbeat_frames = 80
Defensive patterns
Strategy: validation
Validate before calling
from core.config import config
def heartbeat_budget_adequate(estimated_seconds: float) -> bool:
return (config.heartbeat_timeout >= estimated_seconds
and config.max_heartbeat_frames >= int(estimated_seconds / 0.1)) Type guard
def is_heartbeat_timeout(e: BaseException) -> bool:
return (isinstance(e, TimeoutError)
and 'heartbeat frames without payload' in str(e)) Try / catch
try:
resp = conn.send_command(cmd, params)
except TimeoutError as e:
if 'heartbeat frames without payload' in str(e):
# likely a blocking dialog / stuck op in Unity; bump budget and retry once
config.heartbeat_timeout = max(config.heartbeat_timeout, 10.0)
resp = conn.send_command(cmd, params)
else:
raise Prevention
- Watch for Unity modal dialogs and dismiss them before long commands.
- Size max_heartbeat_frames/heartbeat_timeout to your longest legitimate operation.
- Avoid PlayMode scripts with unbounded loops.
When it happens
Trigger: A tool whose Unity-side handler never returns — an infinite loop, a deadlock, or a blocking modal dialog in the editor — so Unity keeps sending heartbeats but never the result frame.
Common situations: Unity showing a blocking modal/input dialog, a PlayMode script stuck in a loop, or a genuinely heavy operation that legitimately exceeds the small default heartbeat budget.
Related errors
- Timeout receiving Unity response
- Command '{command_type}' exceeded total deadline of {total_t
- Preview loading timed out.
- Connection to Unity timed out after {timeout or cfg.timeout}
- Connection to Unity timed out while listing instances. Unity
AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13).
Data as JSON: /api/errors/226fc6670848328e.
Report an issue: GitHub.