Skip to content

Bug: RuntimeError during cleanup on Windows – no running event loop #575

Description

@Dadiya-Harsh

Describe the bug

When running the mcp client using uv run client.py on Windows, the client connects and runs correctly, but after the response is returned, a RuntimeError: no running event loop is thrown during shutdown. The error occurs inside the terminate_windows_process function in win32.py, which uses anyio.fail_after() after the event loop has already closed.


To Reproduce

Steps to reproduce the behavior:

  1. Use the following Python code to create a minimal MCP client that connects to a server and runs a tool-enabled Groq chat query:
# save this as `client.py`importasyncioimportjsonfromcontextlibimportAsyncExitStackimportosfromtypingimportAny, Dict, List, Optionalimportnest_asynciofromdotenvimportload_dotenvfrommcpimportClientSession, StdioServerParametersfrommcp.client.stdioimportstdio_clientfromgroqimportAsyncGroqnest_asyncio.apply()
load_dotenv()
classMCPGroqClient:
def__init__(self, model: str="llama-3.3-70b-versatile"):
self.session: Optional[ClientSession] =Noneself.exit_stack=AsyncExitStack()
self.groq_client=AsyncGroq(api_key=os.getenv("GROQ_API_KEY"))
self.model=modelself.stdio: Optional[Any] =Noneself.write: Optional[Any] =Noneasyncdefconnect_to_server(self, server_script_path: str="server.py"):
server_params=StdioServerParameters(command="python", args=[server_script_path])
stdio_transport=awaitself.exit_stack.enter_async_context(stdio_client(server_params))
self.stdio, self.write=stdio_transportself.session=awaitself.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
awaitself.session.initialize()
tools_result=awaitself.session.list_tools()
print("\nConnected to server with tools:")
fortoolintools_result.tools:
print(f" - {tool.name}: {tool.description}")
asyncdefget_mcp_tools(self) ->List[Dict[str, Any]]:
tools_result=awaitself.session.list_tools()
return [
{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"parameters": tool.inputSchema,
},
}
fortoolintools_result.tools
]
asyncdefprocess_query(self, query: str) ->str:
tools=awaitself.get_mcp_tools()
response=awaitself.groq_client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": query}],
tools=tools,
tool_choice="auto",
)
assistant_message=response.choices[0].messagemessages= [{"role": "user", "content": query}, assistant_message]
ifassistant_message.tool_calls:
fortool_callinassistant_message.tool_calls:
result=awaitself.session.call_tool(
tool_call.function.name,
arguments=json.loads(tool_call.function.arguments),
)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result.content[0].text,
})
final_response=awaitself.groq_client.chat.completions.create(
model=self.model,
messages=messages,
tools=tools,
tool_choice="none",
)
returnfinal_response.choices[0].message.contentreturnassistant_message.contentasyncdefcleanup(self):
awaitself.exit_stack.aclose()
asyncdefmain():
client=MCPGroqClient()
awaitclient.connect_to_server()
query="What is our company's vacation policy?"print(f"\nQuery: {query}")
response=awaitclient.process_query(query)
print(f"\nResponse: {response}")
if__name__=="__main__":
asyncio.run(main())
  1. Set your environment variable GROQ_API_KEY.
  2. Start the server with a valid tool (like knowledge_base).
  3. Run the client using:
    uv run client.py
  4. Observe the traceback at shutdown related to RuntimeError: no running event loop.

Expected behavior

The client should shut down gracefully without throwing exceptions after processing a response.


Screenshots / Traceback

Exception ignored in: <async_generator object stdio_client at 0x000002025AE0EFC0>
Traceback (most recent call last):
File "C:\Program Files\Python312\Lib\asyncio\tasks.py", line 314, in __step_run_and_handle_result
result = coro.send(None)
^^^^^^^^^^^^^^^
RuntimeError: async generator ignored GeneratorExit
Exception ignored in: <coroutine object terminate_windows_process at 0x000002025AF5F3E0>
Traceback (most recent call last):
File "...\Lib\site-packages\mcp\client\stdio\win32.py", line 105, in terminate_windows_process
with anyio.fail_after(2.0):
File "C:\Program Files\Python312\Lib\contextlib.py", line 158, in __exit__
self.gen.throw(value)
File "...\Lib\site-packages\anyio\_core\_tasks.py", line 112, in fail_after
with get_async_backend().create_cancel_scope(
File "...\Lib\site-packages\anyio\_backends\_asyncio.py", line 456, in __exit__
ifcurrent_task() is not self._host_task:
^^^^^^^^^^^^^^
RuntimeError: no running event loop

Desktop (please complete the following information):

  • OS: Windows 11
  • Python Version: 3.12
  • MCP Version: [insert version]
  • AnyIO Version: [insert version]
  • Terminal: Windows PowerShell
  • Command Used:uv run client.py

Additional context

  • This issue seems to be a Windows-specific async shutdown bug.
  • Happens when anyio.fail_after() is used after the event loop is already closed.
  • Suggest adding a check before using async context tools during shutdown, such as:
    ifasyncio.get_event_loop().is_running():
    withanyio.fail_after(2.0):
    ...
  • Or use try/except RuntimeError to suppress it gracefully.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions