Bug report
Bug description:
The following code works fine on Python 3.13, but fails on Python 3.14 with a RuntimeError related to asyncio tasks.
Minimal reproducible example
import asyncio import inspect from functools import wraps from typing import Any, Awaitable, Callable, Union import pytest from multiprocess import Pipe, Process from multiprocess.connection import Connection import multiprocess as mp mp.set_start_method(method="fork", force=True) # "spawn" works fine class SubprocessError: def __init__(self, ex: Exception) -> None: self.exception = ex def in_subprocess[T](func: Callable[..., Union[T, Awaitable[T]]]) -> Callable[..., Awaitable[T]]: @wraps(func) async def wrapper(*args: Any, **kwargs: Any) -> T: return await calculate_in_subprocess(func, *args, **kwargs) return wrapper async def calculate_in_subprocess[T](func: Callable[..., Union[T, Awaitable[T]]], *args: Any, **kwargs: Any) -> T: rx, tx = Pipe(duplex=False) # receiver & transmitter ; Pipe is one-way only process = Process(target=_inner, args=(tx, func, *args), kwargs=kwargs) process.start() event = asyncio.Event() loop = asyncio.get_event_loop() loop.add_reader(fd=rx.fileno(), callback=event.set) if not rx.poll(): # do not use process.is_alive() as condition here await event.wait() loop.remove_reader(fd=rx.fileno()) event.clear() result = rx.recv() process.join() # this blocks synchronously! make sure that process is terminated before you call join() rx.close() tx.close() if isinstance(result, SubprocessError): raise result.exception return result def _inner[T](tx: Connection, fun: Callable[..., Union[T, Awaitable[T]]], *a, **kw_args) -> None: event_loop = None if inspect.iscoroutinefunction(fun): event_loop = asyncio.new_event_loop() asyncio.set_event_loop(event_loop) try: if event_loop is not None: res = event_loop.run_until_complete(fun(*a, **kw_args)) else: res = fun(*a, **kw_args) except Exception as ex: tx.send(SubprocessError(ex=ex)) else: tx.send(res) @pytest.mark.asyncio async def test_in_subprocess_simple_async(): @in_subprocess async def f() -> int: return 42 assert await f() == 42
Error message with Python 3.14
-------------------------------- live log call ---------------------------------
ERROR asyncio:base_events.py:1875 Exception in callback <_asyncio.TaskStepMethWrapper object at 0x7e71ba729ff0>()
handle: <Handle <_asyncio.TaskStepMethWrapper object at 0x7e71ba729ff0>()>
Traceback (most recent call last):
File "/usr/lib/python3.14/asyncio/events.py", line 94, in _run
self._context.run(self._callback, *self._args)
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: Cannot enter into task <Task pending name='Task-2' coro=<test_in_subprocess_simple_async.<locals>.f() running at foo.py:73> cb=[_run_until_complete_cb() at /usr/lib/python3.14/asyncio/base_events.py:181]> while another task <Task pending name='Task-1' coro=<test_in_subprocess_simple_async() running at foo.py:77> cb=[_run_until_complete_cb() at /usr/lib/python3.14/asyncio/base_events.py:181]> is being executed.
Environment
Installed packages (note: multiprocess must be installed from GitHub):
certifi==2025.10.5
charset-normalizer==3.4.3
dill==0.4.0
docker==7.1.0
idna==3.10
iniconfig==2.1.0
multiprocess @ git+https://github.com/uqfoundation/multiprocess.git@02ea4bd36cac5013d70847815c92e1a736ef4a05
packaging==25.0
pluggy==1.6.0
Pygments==2.19.2
pytest==8.4.2
pytest-asyncio==1.2.0
pytest_docker_tools==3.1.9
requests==2.32.5
urllib3==2.5.0
And before you say, that the error is in the third-party lib multiprocess and I should use multiprocessing instead of multiprocess: The bug even occurs with multiprocessing (just replace the lib in the code example above). In Python 3.13 it will fail with a PicklingError (which is expected for this example) but in Python 3.14 the RuntimeError: Cannot enter into task will be thrown (see above). So this issue must be in Python 3.14.
CPython versions tested on:
3.14
Operating systems tested on:
Linux