agentscope-ai/agentscope · warning · RuntimeError
gateway failed to remove MCP {self.name!r}: {_safe_detail(st
Error message
gateway failed to remove MCP {self.name!r}: {_safe_detail(status, resp_body)} What it means
Raised by GatewayMCPClient.close() when the gateway HTTP DELETE /mcps/{name} request returns a 4xx/5xx status and ignore_errors=False. The gateway process inside the sandbox failed to deregister the MCP server (e.g. already removed, unknown agent/session id, or gateway internal error). _safe_detail embeds the HTTP status and a truncated response body so the upstream reason is visible.
Source
Thrown at src/agentscope/workspace/_gateway_client.py:296
"""
if not self._is_connected:
if ignore_errors:
return
raise RuntimeError(
f"MCP {self.name!r} is not connected. Call connect() first.",
)
assert self._gateway is not None
try:
status, resp_body = await self._gateway.exec_request(
"DELETE",
f"/mcps/{self.name}",
params={
"agent_id": self._agent_id,
"session_id": self._session_id,
},
)
if status >= 400 and not ignore_errors:
raise RuntimeError(
f"gateway failed to remove MCP {self.name!r}: "
f"{_safe_detail(status, resp_body)}",
)
except Exception:
if not ignore_errors:
raise
self._is_connected = False
# ── tool discovery ────────────────────────────────────────────
async def list_raw_tools(self) -> list[mcp.types.Tool]:
"""Fetch upstream tools via ``GET /mcps/{name}/tools``.
Returns raw :class:`mcp.types.Tool` descriptors (upstream names,
no ``mcp__`` prefix) so the inherited :meth:`get_tool` can
re-wrap them like a local :class:`MCPClient`. The unfiltered
list is cached; the returned list has ``enable_tools`` /
``disable_tools`` applied.View on GitHub (pinned to e90f1c7592)
Solutions
- Use the default `await client.close()` (ignore_errors=True) in shutdown/cleanup paths — it suppresses 4xx/5xx by design
- Check gateway health (`await gateway.health()`) before closing if you must pass ignore_errors=False
- Re-list MCPs (`await gateway.list_mcps(agent_id, session_id)`) and only close clients that still appear
- Inspect the embedded status/body detail in the message to see the gateway's actual complaint (404 vs 500) and fix the agent_id/session_id accordingly
Example fix
// before await client.close(ignore_errors=False) // after await client.close() # ignore_errors=True by default; safe for cleanup
Defensive patterns
Strategy: try-catch
Validate before calling
alive = await gateway.list_mcps(agent_id, session_id) registered = any(c.name == client.name for c in alive)
Try / catch
try:
await client.close(ignore_errors=False)
except RuntimeError as e:
logger.warning("MCP deregistration failed (continuing): %s", e) Prevention
- Default to close() with ignore_errors=True in all cleanup paths
- Treat close as best-effort: the gateway also reaps MCPs when sessions end
- Health-check the gateway before strict closes
When it happens
Trigger: Calling `await client.close(ignore_errors=False)` on a connected GatewayMCPClient after the gateway has already dropped the MCP (restart, expiry), or with an agent_id/session_id that no longer matches a registered MCP. Any DELETE /mcps/{name} returning status >= 400.
Common situations: Gateway restarted or crashed between connect() and close(); session ids recycled across runs; calling close() twice with different id scopes; passing ignore_errors=False in cleanup paths where the gateway state is already stale.
Related errors
- gateway failed to list tools for MCP {self.name!r}: {_safe_d
- gateway failed to list MCPs: {_safe_detail(status, body)}
- {name!r} not found for agent={agent_id!r} session={session_i
- name required
- {name!r} already exists for agent={agent_id!r} session={sess
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/fff0f2fb97850c42.
Report an issue: GitHub.