agentscope-ai/agentscope · error · RuntimeError
MCP '{self.name}' is not connected. Call connect() first.
Error message
MCP '{self.name}' is not connected. Call connect() first. What it means
close() on a stateful MCPClient raises if the client is not currently connected. Since stateless MCPs skip this check, this specifically guards stateful sessions from closing an already-closed or never-connected transport stack, which would otherwise be a no-op masking lifecycle bugs.
Source
Thrown at src/agentscope/mcp/_mcp_client.py:280
"""Close the MCP connection (for stateful connections only).
For stateless connections, this method does nothing.
Args:
ignore_errors: Whether to ignore errors during cleanup.
Raises:
RuntimeError: If not connected.
"""
if not self.is_stateful:
logger.debug(
"Stateless MCP '%s' does not require explicit close.",
self.name,
)
return
if not self._is_connected:
raise RuntimeError(
f"MCP '{self.name}' is not connected. "
"Call connect() first.",
)
try:
await self._stack.aclose()
except Exception as e:
if not ignore_errors:
raise e
logger.warning(
"Error closing MCP '%s': %s",
self.name,
str(e),
)
finally:
self._client = None
self._stack = None
self._session = NoneView on GitHub (pinned to e90f1c7592)
Solutions
- Track connection state and only close when connected, or check _is_connected before calling close()
- Wrap close() in try/except RuntimeError for unconditional cleanup paths
- Ensure close() is called exactly once, e.g. via a finally block around the connected region
Example fix
// before
finally:
await client.close() # raises 227 if connect() failed or already closed
// after
finally:
if client._is_connected:
await client.close() Defensive patterns
Strategy: validation
Validate before calling
if getattr(client, "_is_connected", False):
await client.close() Try / catch
try:
await client.close()
except RuntimeError as e:
if "not connected" in str(e):
pass # already closed; nothing to do
else:
raise Prevention
- Check connection state before close in cleanup paths
- Close exactly once, in the finally block that owns the connect
- Idempotent shutdown helper: async def safe_close(c): if c._is_connected: await c.close()
When it happens
Trigger: await client.close() without a prior successful connect(); or close() called twice (the first close resets _is_connected); or close() after connect() failed midway.
Common situations: Cleanup paths in finally blocks that run even when setup failed; agent shutdown routines that close all MCPs regardless of connection state; double-shutdown in tests.
Related errors
- MCP '{self.name}' is already connected. Call close() before
- STDIO MCP does not support ephemeral mode. Use 'shared' or '
- Invalid values for MCP {card.name!r}: {e.message}
- MCP {card.name!r} needs a value for: {', '.join(sorted(missi
- MCP {card.name!r} produced an invalid client: {e}
AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28).
Data as JSON: /api/errors/ce3d8c6c44dcaff7.
Report an issue: GitHub.