{"record":{"id":"44a1bb3a1fa0fb1a","repo":"CoplayDev/unity-mcp","slug":"failed-to-list-unity-instances","errorCode":null,"errorMessage":"Failed to list Unity instances","messagePattern":"Failed to list Unity instances","errorType":"exception","errorClass":"UnityConnectionError","httpStatus":null,"severity":"error","filePath":"Server/src/cli/utils/connection.py","lineNumber":212,"sourceCode":"    except httpx.ConnectError as e:\n        raise UnityConnectionError(\n            f\"Cannot connect to Unity MCP server at {cfg.host}:{cfg.port}. \"\n            f\"Make sure the server is running and Unity is connected.\\n\"\n            f\"Error: {e}\"\n        )\n    except httpx.TimeoutException:\n        raise UnityConnectionError(\n            \"Connection to Unity timed out while listing instances. \"\n            \"Unity may be busy or unresponsive.\"\n        )\n    except httpx.HTTPStatusError as e:\n        raise UnityConnectionError(\n            f\"HTTP error from server: {e.response.status_code} - {e.response.text}\"\n        )\n    except Exception as e:\n        raise UnityConnectionError(f\"Unexpected error: {e}\")\n\n    raise UnityConnectionError(\"Failed to list Unity instances\")\n\n\ndef run_list_instances(config: Optional[CLIConfig] = None) -> Dict[str, Any]:\n    \"\"\"Synchronous wrapper for list_unity_instances.\"\"\"\n    return asyncio.run(list_unity_instances(config))\n\n\nasync def list_custom_tools(config: Optional[CLIConfig] = None) -> Dict[str, Any]:\n    \"\"\"List custom tools registered for the active Unity project.\"\"\"\n    cfg = config or get_config()\n    url = f\"http://{cfg.host}:{cfg.port}/api/custom-tools\"\n    params: Dict[str, Any] = {}\n    if cfg.unity_instance:\n        params[\"instance\"] = cfg.unity_instance\n\n    try:\n        async with httpx.AsyncClient() as client:\n            response = await client.get(url, params=params, timeout=cfg.timeout)","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/Server/src/cli/utils/connection.py#L194-L230","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Manually curl http://127.0.0.1:<port>/api/instances and inspect the JSON keys to see what shape the server actually returns.","Align CLI and server versions — the 'instances' key is part of the contract; a missing key usually means version mismatch.","If developing the server, ensure the /api/instances handler always returns {\"instances\": [...]} even when the list is empty.","If behind a proxy, bypass it temporarily to confirm the raw server response."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import httpx\nasync def validate_instances_response(host: str, port: int) -> dict | None:\n    async with httpx.AsyncClient() as c:\n        r = await c.get(f\"http://{host}:{port}/api/instances\", timeout=10)\n        r.raise_for_status()\n        data = r.json()\n        if \"instances\" not in data:\n            print(f\"Unexpected response shape. Keys: {list(data.keys())}\")\n            return None\n        return data","typeGuard":"def is_instances_payload(data: object) -> bool:\n    return isinstance(data, dict) and \"instances\" in data and isinstance(data[\"instances\"], list)","tryCatchPattern":"from cli.utils.connection import UnityConnectionError\ntry:\n    data = run_list_instances(config)\nexcept UnityConnectionError as e:\n    if \"Failed to list Unity instances\" in str(e):\n        # Response shape mismatch — check server/CLI version alignment\n        print(\"Server response missing 'instances' key. Verify version match.\")\n    else:\n        raise","preventionTips":["Align CLI and server versions so the /api/instances response contract matches.","When developing the server, always return {\"instances\": [...]} even if empty.","In tests, assert the response shape before relying on the data."],"tags":["network","protocol-mismatch","cli","httpx","response-shape"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}