Skip to content

MCPServerStreamableHttp connection error corrupt next async calls #1288

@MikhailIzvekov

Description

@MikhailIzvekov

Describe the bug

If you try to connect to mcp server that is not currently available, you will ruin subsequent await calls (and can't gracefully shutdown ever). Also the actual information about what happened (can't connect) is buried deep in exceptions chain.

Debug information

openai-agents==0.2.3
Python 3.13.5

Repro steps

import asyncio

from agents.mcp import MCPServerStreamableHttpParams, MCPServerStreamableHttp


async def do_other_stuff():
    await asyncio.sleep(2)


async def main():
    params = MCPServerStreamableHttpParams(url='http://localhost:3002')  # I have no mcp server at this address.
    server = MCPServerStreamableHttp(params)
    try:
        async with server:
            pass
    except BaseException as e:
        print(e)
        # await server.cleanup()  # workaround, but root cause will be lost

    print('lets do other stuff')
    await do_other_stuff()  # <-  will cause "Cancelled by cancel scope ..."
    print('done')

if __name__ == "__main__":
    asyncio.run(main())

Expected behavior

httpcore.ConnectError: All connection attempts failed
lets do other stuff
done

Actual behavior

Cancelled by cancel scope 181bc7de3c0
lets do other stuff
an error occurred during closing of asynchronous generator <async_generator object streamablehttp_client at 0x00000181BC6079C0>
asyncgen: <async_generator object streamablehttp_client at 0x00000181BC6079C0>
  + Exception Group Traceback (most recent call last):
  |   File "[REDACTED]\venvs\dsa\Lib\site-packages\anyio\_backends\_asyncio.py", line 772, in __aexit__
  |     raise BaseExceptionGroup(
  |         "unhandled errors in a TaskGroup", self._exceptions
  |     ) from None
  | BaseExceptionGroup: unhandled errors in a TaskGroup (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 101, in map_httpcore_exceptions
    |     yield
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 394, in handle_async_request
    |     resp = await self._pool.handle_async_request(req)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection_pool.py", line 256, in handle_async_request
    |     raise exc from None
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection_pool.py", line 236, in handle_async_request
    |     response = await connection.handle_async_request(
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |         pool_request.request
    |         ^^^^^^^^^^^^^^^^^^^^
    |     )
    |     ^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection.py", line 101, in handle_async_request
    |     raise exc
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection.py", line 78, in handle_async_request
    |     stream = await self._connect(request)
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_async\connection.py", line 124, in _connect
    |     stream = await self._network_backend.connect_tcp(**kwargs)
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_backends\auto.py", line 31, in connect_tcp
    |     return await self._backend.connect_tcp(
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     ...<5 lines>...
    |     )
    |     ^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_backends\anyio.py", line 113, in connect_tcp
    |     with map_exceptions(exc_map):
    |          ~~~~~~~~~~~~~~^^^^^^^^^
    |   File "[REDACTED]\Python313\Lib\contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpcore\_exceptions.py", line 14, in map_exceptions
    |     raise to_exc(exc) from exc
    | httpcore.ConnectError: All connection attempts failed
    | 
    | The above exception was the direct cause of the following exception:
    | 
    | Traceback (most recent call last):
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 405, in handle_request_async
    |     await self._handle_post_request(ctx)
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 259, in _handle_post_request
    |     async with ctx.client.stream(
    |                ~~~~~~~~~~~~~~~~~^
    |         "POST",
    |         ^^^^^^^
    |     ...<2 lines>...
    |         headers=headers,
    |         ^^^^^^^^^^^^^^^^
    |     ) as response:
    |     ^
    |   File "[REDACTED]\Python313\Lib\contextlib.py", line 214, in __aenter__
    |     return await anext(self.gen)
    |            ^^^^^^^^^^^^^^^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1583, in stream
    |     response = await self.send(
    |                ^^^^^^^^^^^^^^^^
    |     ...<4 lines>...
    |     )
    |     ^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1629, in send
    |     response = await self._send_handling_auth(
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     ...<4 lines>...
    |     )
    |     ^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1657, in _send_handling_auth
    |     response = await self._send_handling_redirects(
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     ...<3 lines>...
    |     )
    |     ^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1694, in _send_handling_redirects
    |     response = await self._send_single_request(request)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_client.py", line 1730, in _send_single_request
    |     response = await transport.handle_async_request(request)
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 393, in handle_async_request
    |     with map_httpcore_exceptions():
    |          ~~~~~~~~~~~~~~~~~~~~~~~^^
    |   File "[REDACTED]\Python313\Lib\contextlib.py", line 162, in __exit__
    |     self.gen.throw(value)
    |     ~~~~~~~~~~~~~~^^^^^^^
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\httpx\_transports\default.py", line 118, in map_httpcore_exceptions
    |     raise mapped_exc(message) from exc
    | httpx.ConnectError: All connection attempts failed
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 498, in streamablehttp_client
    |     yield (
    |     ...<3 lines>...
    |     )
    | GeneratorExit
    +------------------------------------

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "[REDACTED]\venvs\dsa\Lib\site-packages\mcp\client\streamable_http.py", line 474, in streamablehttp_client
    async with anyio.create_task_group() as tg:
               ~~~~~~~~~~~~~~~~~~~~~~~^^
  File "[REDACTED]\venvs\dsa\Lib\site-packages\anyio\_backends\_asyncio.py", line 778, in __aexit__
    if self.cancel_scope.__exit__(type(exc), exc, exc.__traceback__):
       ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "[REDACTED]\venvs\dsa\Lib\site-packages\anyio\_backends\_asyncio.py", line 457, in __exit__
    raise RuntimeError(
    ...<2 lines>...
    )
RuntimeError: Attempted to exit cancel scope in a different task than it was entered in
Traceback (most recent call last):
  File "[REDACTED]\scratch_2.py", line 25, in <module>
    asyncio.run(main())
    ~~~~~~~~~~~^^^^^^^^
  File "[REDACTED]\Python313\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "[REDACTED]\Python313\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "[REDACTED]\Python313\Lib\asyncio\base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "[REDACTED]\scratch_2.py", line 21, in main
    await do_other_stuff()  # <-  will cause "Cancelled by cancel scope ..."
    ^^^^^^^^^^^^^^^^^^^^^^
  File "[REDACTED]\scratch_2.py", line 7, in do_other_stuff
    await asyncio.sleep(2)
  File "[REDACTED]\Python313\Lib\asyncio\tasks.py", line 718, in sleep
    return await future
           ^^^^^^^^^^^^
asyncio.exceptions.CancelledError: Cancelled by cancel scope 181bc7de3c0

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions