{"record":{"id":"ce3d8c6c44dcaff7","repo":"agentscope-ai/agentscope","slug":"mcp-self-name-is-not-connected-call-connect","errorCode":null,"errorMessage":"MCP '{self.name}' is not connected. Call connect() first.","messagePattern":"MCP '(.+?)' is not connected\\. Call connect\\(\\) first\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/agentscope/mcp/_mcp_client.py","lineNumber":280,"sourceCode":"        \"\"\"Close the MCP connection (for stateful connections only).\n\n        For stateless connections, this method does nothing.\n\n        Args:\n            ignore_errors: Whether to ignore errors during cleanup.\n\n        Raises:\n            RuntimeError: If not connected.\n        \"\"\"\n        if not self.is_stateful:\n            logger.debug(\n                \"Stateless MCP '%s' does not require explicit close.\",\n                self.name,\n            )\n            return\n\n        if not self._is_connected:\n            raise RuntimeError(\n                f\"MCP '{self.name}' is not connected. \"\n                \"Call connect() first.\",\n            )\n\n        try:\n            await self._stack.aclose()\n        except Exception as e:\n            if not ignore_errors:\n                raise e\n            logger.warning(\n                \"Error closing MCP '%s': %s\",\n                self.name,\n                str(e),\n            )\n        finally:\n            self._client = None\n            self._stack = None\n            self._session = None","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/agentscope-ai/agentscope/blob/e90f1c7592896cc95f6e5ee506194f533378247d/src/agentscope/mcp/_mcp_client.py#L262-L298","documentation":"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.","triggerScenarios":"await client.close() without a prior successful connect(); or close() called twice (the first close resets _is_connected); or close() after connect() failed midway.","commonSituations":"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.","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"],"exampleFix":"// before\nfinally:\n    await client.close()  # raises 227 if connect() failed or already closed\n\n// after\nfinally:\n    if client._is_connected:\n        await client.close()","handlingStrategy":"validation","validationCode":"if getattr(client, \"_is_connected\", False):\n    await client.close()","typeGuard":null,"tryCatchPattern":"try:\n    await client.close()\nexcept RuntimeError as e:\n    if \"not connected\" in str(e):\n        pass  # already closed; nothing to do\n    else:\n        raise","preventionTips":["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()"],"tags":["mcp","connection-lifecycle","asyncio"],"backgroundTag":"close-before-connect-error","analyzedSha":"e90f1c7592896cc95f6e5ee506194f533378247d","analyzedAt":"2026-08-28T18:24:12.087Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}