rohitg00/ai-engineering-from-scratch · error · RuntimeError
: bounded legacy probe failed closed
Error message
{peer.name}: bounded legacy probe failed closed What it means
The legacy initialize probe was sent with a bounded timeout (legacy_probe_timeout_ms) and the transport raised TimeoutError or ConnectionError. The client deliberately fails closed rather than retrying indefinitely, wrapping the transport error to identify which peer and which stage failed.
Solutions
- Raise legacy_probe_timeout_ms on the client to cover the server's startup latency
- Verify the peer process is alive and listening before connect_all
- Check network path/firewall between client and peer
- Confirm the server actually speaks the legacy initialize method at all
Example fix
// before client = McpClient(..., legacy_probe_timeout_ms=500) // after client = McpClient(..., legacy_probe_timeout_ms=5000)
Defensive patterns
Strategy: retry
Validate before calling
if not peer_reachable(peer):
raise SystemExit(f"{peer.name} unreachable before connect") Type guard
null
Try / catch
try:
client.connect_all()
except RuntimeError as e:
if "legacy probe failed closed" in str(e) and transient_network():
client.connect_all() # one bounded retry after fixing the network Prevention
- Size legacy_probe_timeout_ms to the slowest observed server startup
- Health-check peers before connect_all
When it happens
Trigger: peer.allow_legacy is True, _probe_legacy sends legacy_request('initialize') via self._send(peer, ..., self.legacy_probe_timeout_ms), and the send times out or the connection drops before a response arrives.
Common situations: Legacy server that is slow to answer initialize; dead peer process behind a still-open socket; firewall dropping responses; legacy_probe_timeout_ms configured too low for a cold-starting server.
Related errors
- : proven-modern discovery retry failed
- : bounded legacy probe returned no result
- : legacy initialize returned an error
- : malformed legacy initialize result
- : ; legacy compatibility is not allowlisted
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/e123090fc45e0758.
Report an issue: GitHub.
Appendix: source
Thrown at phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py:371
def _probe_legacy(self, peer: Peer, trigger: str) -> None:
if not peer.allow_legacy:
raise RuntimeError(
f"{peer.name}: {trigger}; legacy compatibility is not allowlisted"
)
request_id = self._new_id()
initialize = legacy_request(
request_id,
"initialize",
{
"protocolVersion": self.supported_legacy[0],
"capabilities": self.client_capabilities.copy(),
"clientInfo": CLIENT_INFO.copy(),
},
)
try:
response = self._send(peer, initialize, self.legacy_probe_timeout_ms)
except (TimeoutError, ConnectionError) as exc:
raise RuntimeError(f"{peer.name}: bounded legacy probe failed closed") from exc
if not isinstance(response, dict):
raise RuntimeError(f"{peer.name}: bounded legacy probe returned no result")
kind, payload = decode_rpc_response(response, request_id)
if kind != "result":
raise RuntimeError(f"{peer.name}: legacy initialize returned an error")
result = payload
version = result.get("protocolVersion")
capabilities = result.get("capabilities")
server_info = result.get("serverInfo")
valid_server_info = (
isinstance(server_info, dict)
and isinstance(server_info.get("name"), str)
and bool(server_info["name"])
and isinstance(server_info.get("version"), str)
and bool(server_info["version"])
)
if version not in self.supported_legacy:
raise RuntimeError(f"{peer.name}: unsupported legacy protocol revision")View on GitHub (pinned to 39ea8a1c6d)