rohitg00/ai-engineering-from-scratch · error · RuntimeError
{peer.name}: {trigger}; legacy compatibility is not allowlis
Error message
{peer.name}: {trigger}; legacy compatibility is not allowlisted What it means
Raised when the MCP client's fallback path tries to downgrade a peer to the legacy protocol but the peer was not configured with allow_legacy=True. The client treats legacy compatibility as an opt-in trust decision, so it fails closed instead of silently negotiating an older, less-verified protocol revision.
Source
Thrown at phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py:355
) -> dict[str, Any] | None:
return peer.transport(message, timeout_ms)
def _mutual_version(self, advertised: list[Any]) -> str | None:
common = [version for version in advertised if version in self.supported_modern]
return sorted(common, reverse=True)[0] if common else None
def _activate_modern(self, peer: Peer, result: dict[str, Any], version: str) -> None:
if result.get("resultType") != "complete":
raise RuntimeError(f"{peer.name}: modern discovery omitted resultType")
peer.era = "modern"
peer.protocol_version = version
peer.capabilities = result.get("capabilities", {})
peer.server_info = result.get("_meta", {}).get(SERVER_INFO_KEY, {})
peer.available = True
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")View on GitHub (pinned to 39ea8a1c6d)
Solutions
- Set allow_legacy=True on the Peer if you intentionally need to talk to the legacy server
- Upgrade the peer server to a modern protocol revision that answers server/discover
- Check why modern discovery failed (empty response, unrecognized error code) and fix the server side
- Keep legacy off in production and treat this error as a signal the server is outdated
Example fix
// before peer = Peer(name="old-server", transport=t) # allow_legacy defaults to False client.connect_all() # -> RuntimeError: legacy compatibility is not allowlisted // after peer = Peer(name="old-server", transport=t, allow_legacy=True) client.connect_all()
Defensive patterns
Strategy: validation
Validate before calling
for peer in client.peers.values():
if not peer.allow_legacy and peer_likely_legacy(peer):
peer.allow_legacy = True # explicit opt-in before connect_all Type guard
null
Try / catch
try:
client.connect_all()
except RuntimeError as e:
if "legacy compatibility is not allowlisted" in str(e):
# decide: opt in to legacy or upgrade the peer
... Prevention
- Declare legacy support per peer at configuration time, not at connect time
- Keep a manifest of which peers speak which protocol era
When it happens
Trigger: Modern discovery on a peer returns an empty/unrecognized result or an unrecognized discovery error code, _connect_peer calls _probe_legacy(peer, trigger), and peer.allow_legacy is falsy.
Common situations: Pointing the client at an old MCP server that only speaks a legacy initialize handshake; forgetting to set allow_legacy in the Peer config; server upgrade changing discovery behavior so the legacy fallback path suddenly engages.
Related errors
- {peer.name}: bounded legacy probe failed closed
- {peer.name}: bounded legacy probe returned no result
- {peer.name}: legacy initialize returned an error
- {peer.name}: unsupported legacy protocol revision
- {peer.name}: malformed legacy initialize result
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/bb9e6f5928f21fb6.
Report an issue: GitHub.