{"record":{"id":"26eded0ebda9b08f","repo":"rohitg00/ai-engineering-from-scratch","slug":"peer-name-missing-response","errorCode":null,"errorMessage":"{peer.name}: missing response","messagePattern":"(.+?): missing response","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py","lineNumber":488,"sourceCode":"            self._connect_peer(self.peers[peer_name])\n\n    def _request(self, peer: Peer, method: str, params: dict[str, Any]) -> dict[str, Any]:\n        request_id = self._new_id()\n        if peer.era == \"modern\":\n            message = modern_request(\n                request_id,\n                method,\n                params,\n                peer.protocol_version or PROTOCOL_VERSION,\n                self.client_capabilities,\n            )\n        elif peer.era == \"legacy\":\n            message = legacy_request(request_id, method, params)\n        else:\n            raise RuntimeError(f\"{peer.name}: protocol era not selected\")\n        response = self._send(peer, message)\n        if not isinstance(response, dict):\n            raise RuntimeError(f\"{peer.name}: missing response\")\n        kind, payload = decode_rpc_response(response, request_id)\n        if kind != \"result\":\n            raise RuntimeError(f\"{peer.name}: RPC error {payload}\")\n        result = dict(payload)\n        if peer.era == \"modern\" and \"resultType\" not in result:\n            raise RuntimeError(f\"{peer.name}: modern result omitted resultType\")\n        if peer.era == \"legacy\":\n            result.setdefault(\"resultType\", \"complete\")\n        return result\n\n    def discover_tools(self) -> None:\n        for peer_name in sorted(self.peers):\n            peer = self.peers[peer_name]\n            if peer.available:\n                result = self._request(peer, \"tools/list\", {})\n                peer.tools = sorted(result.get(\"tools\", []), key=lambda tool: tool[\"name\"])\n\n    def merge(self, policy: str = \"prefix-on-collision\") -> None:","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py#L470-L506","documentation":"A request completed at the transport level but _send returned a non-dict, so the client cannot decode a JSON-RPC response. This is the steady-state request path (tools/list, tools/call) rather than the handshake paths.","triggerScenarios":"self._send(peer, message) inside _request returns something for which isinstance(response, dict) is False.","commonSituations":"Peer disconnecting mid-request so the reader yields None; transport leaking raw text; test stubs returning the wrong type.","solutions":["Check peer liveness before issuing requests","Fix the transport read path to parse frames into dicts","Reconnect the peer (re-run connect_all) if the socket dropped","Correct test doubles to return dict responses"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"if not peer_still_connected(peer):\n    client.connect_all()  # re-handshake before requesting","typeGuard":"def is_rpc_message(value) -> bool:\n    return isinstance(value, dict) and (\"result\" in value or \"error\" in value)","tryCatchPattern":"try:\n    client.discover_tools()\nexcept RuntimeError as e:\n    if \"missing response\" in str(e):\n        client.connect_all(); client.discover_tools()","preventionTips":["Check peer liveness before long-lived sessions","Make transports return None-safe parsed dicts only"],"tags":["mcp","transport","response-shape"],"backgroundTag":"malformed-rpc-response","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}