CoplayDev/unity-mcp · error · UnityConnectionError

Failed to list Unity instances

Error message

Failed to list Unity instances

What it means

The terminal fallback raise at the end of list_unity_instances(), reached only when the try block completes without returning and without raising. This happens exclusively when the HTTP GET succeeds (status 200) and response.json() parses, but the resulting dict does NOT contain an 'instances' key — the guard `if 'instances' in data: return data` fails, so the function falls through. It signals a protocol/shape mismatch: the server is up but returned an unexpected response body.

Source

Thrown at Server/src/cli/utils/connection.py:212

    except httpx.ConnectError as e:
        raise UnityConnectionError(
            f"Cannot connect to Unity MCP server at {cfg.host}:{cfg.port}. "
            f"Make sure the server is running and Unity is connected.\n"
            f"Error: {e}"
        )
    except httpx.TimeoutException:
        raise UnityConnectionError(
            "Connection to Unity timed out while listing instances. "
            "Unity may be busy or unresponsive."
        )
    except httpx.HTTPStatusError as e:
        raise UnityConnectionError(
            f"HTTP error from server: {e.response.status_code} - {e.response.text}"
        )
    except Exception as e:
        raise UnityConnectionError(f"Unexpected error: {e}")

    raise UnityConnectionError("Failed to list Unity instances")


def run_list_instances(config: Optional[CLIConfig] = None) -> Dict[str, Any]:
    """Synchronous wrapper for list_unity_instances."""
    return asyncio.run(list_unity_instances(config))


async def list_custom_tools(config: Optional[CLIConfig] = None) -> Dict[str, Any]:
    """List custom tools registered for the active Unity project."""
    cfg = config or get_config()
    url = f"http://{cfg.host}:{cfg.port}/api/custom-tools"
    params: Dict[str, Any] = {}
    if cfg.unity_instance:
        params["instance"] = cfg.unity_instance

    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(url, params=params, timeout=cfg.timeout)

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Manually curl http://127.0.0.1:<port>/api/instances and inspect the JSON keys to see what shape the server actually returns.
  2. Align CLI and server versions — the 'instances' key is part of the contract; a missing key usually means version mismatch.
  3. If developing the server, ensure the /api/instances handler always returns {"instances": [...]} even when the list is empty.
  4. If behind a proxy, bypass it temporarily to confirm the raw server response.
Defensive patterns

Strategy: validation

Validate before calling

import httpx
async def validate_instances_response(host: str, port: int) -> dict | None:
    async with httpx.AsyncClient() as c:
        r = await c.get(f"http://{host}:{port}/api/instances", timeout=10)
        r.raise_for_status()
        data = r.json()
        if "instances" not in data:
            print(f"Unexpected response shape. Keys: {list(data.keys())}")
            return None
        return data

Type guard

def is_instances_payload(data: object) -> bool:
    return isinstance(data, dict) and "instances" in data and isinstance(data["instances"], list)

Try / catch

from cli.utils.connection import UnityConnectionError
try:
    data = run_list_instances(config)
except UnityConnectionError as e:
    if "Failed to list Unity instances" in str(e):
        # Response shape mismatch — check server/CLI version alignment
        print("Server response missing 'instances' key. Verify version match.")
    else:
        raise

Prevention

When it happens

Trigger: The server responds 200 with a top-level wrapper like {"data": {...}} or {"result": [...]} instead of the expected {"instances": [...]}; an older or newer server version changed the response envelope; a middleware or reverse proxy rewrote the response; the endpoint returned an empty object {} due to a server-side serialization bug.

Common situations: Server/CLI version skew where the response contract changed; a load balancer or API gateway stripped or rewrote the JSON; a development build of the server that returns a different shape; the endpoint handler has a bug that returns an empty dict on a partial failure.

Related errors


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