Repository navigation
Cannot cleanly kill a subprocess using high-level asyncio APIs #88050
Description
Activity
There doesn't appear to be a way to prematurely kill a subprocess using the high-level asyncio subprocess APIs (https://docs.python.org/3.9/library/asyncio-subprocess.html) without getting a traceback on exit.
On exit, the attached program writes the following to stderr:
$ python3.9 kill_subprocess.py
Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x1065f0dc0> Traceback (most recent call last): ... raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closedIf I uncomment
# process._transport.close()or commentasyncio.sleep(1), the walkback disappears. (I get the same behavior in python 3.8. I haven't tried other python versions.)- added3.8 (EOL)end of lifeend of life3.9 (EOL)end of lifeend of lifetype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Apr 19, 2021 Reproducing the program here:
import asyncio async def test(): process = await asyncio.create_subprocess_shell( "sleep 2 && echo done", stdout=asyncio.subprocess.PIPE, ) await asyncio.sleep(1) process.kill() await process.wait() # process._transport.close() asyncio.run(test())
Can I use the high-level API to kill a subprocess cleanly without having to access the protected member process._transport? Seems like an oversight perhaps?
Running kill_subprocess.py on Windows 10, I get these results:
Python 3.7.2 (tags/v3.7.2:9a3ffc0492)
- raises NotImplementedError in base_events.py, _make_subprocess_transport
Python 3.8.2 (tags/v3.8.2:7b3ab59)
- Success
Python 3.9.0 (tags/v3.9.0:9cf6752)
- Success
Python 3.10.0a6 (tags/v3.10.0a6:cc12888)
- SuccessWhat is your OS?
I see this on MacOS and Linux, but I suspect any Unix-like system would have the same behavior.
Reacted by byubeanI'm also experiencing this, with virtually identical code, on macOS 10.15.7. The given fix (process._transport.close()) also works for me, so I'm just using that workaround for the time being.
- added3.11only security fixesonly security fixesand removed3.8 (EOL)end of lifeend of life
on Feb 28, 2022 25 remaining items
- added a commit that references this issue
on Oct 6, 2022 - added a commit that references this issue
on Oct 8, 2022 Is this meant to be fixed in 3.11+?
Yes
Which patch? Not in 3.11.0 as far as I can tell.
#32073 and a followup are present in 3.11.1. They may not be present in 3.11.0.
Hmm. I came here using Google while I'm on 3.11.2 already. This happens sometimes for me on a subprocess wrapped inside a task and stdin/stderr connected. It also only triggers when I am terminating a process myself, instead killing it or letting the loop killing it.
Found this when hitting random test failures on 3.11.2 and created the minimal reproducer below.
import asyncio import logging async def myfunc_inner() -> None: subprocess_task = asyncio.create_task( asyncio.create_subprocess_exec( "sleep", "10", # seems to be relevant; unable to trigger without stdout/stderr connected stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) ) proc = await subprocess_task try: await asyncio.wait_for(proc.communicate(), timeout=0.02) except (asyncio.TimeoutError, asyncio.CancelledError): logging.info("Timeout on subprocess command, terminating.") # unable to trigger with proc.kill() or without terminating it. proc.terminate() return else: logging.info(f"process exited with {proc.returncode}]") # unable to trigger without nested task. async def myfunc_outer() -> None: await asyncio.wait_for(myfunc_inner(), timeout=1.0) if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) loop = asyncio.get_event_loop_policy().new_event_loop() loop.run_until_complete(myfunc_outer()) # This fixes it, but hides the underlying problem I think. # loop.run_until_complete(asyncio.sleep(0.01)) loop.close()
Running this in a shell loop 200 times, this gives ~ 1-10 failures per loop, looks random.
$ for i in `seq 1 200`; do python proctimeout.py; done DEBUG:asyncio:Using selector: EpollSelector Timeout on subprocess command, terminating. DEBUG:asyncio:Using selector: EpollSelector Timeout on subprocess command, terminating. DEBUG:asyncio:Using selector: EpollSelector Timeout on subprocess command, terminating. Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x7f5bff42a2a0> Traceback (most recent call last): File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_subprocess.py", line 126, in __del__ self.close() File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_subprocess.py", line 104, in close proto.pipe.close() File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/unix_events.py", line 558, in close self._close(None) File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/unix_events.py", line 582, in _close self._loop.call_soon(self._call_connection_lost, exc) File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_events.py", line 761, in call_soon self._check_closed() File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_events.py", line 519, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed DEBUG:asyncio:Using selector: EpollSelector Timeout on subprocess command, terminating. WARNING:asyncio:Loop <_UnixSelectorEventLoop running=False closed=True debug=False> that handles pid 419211 is closed Exception ignored in: <function BaseSubprocessTransport.__del__ at 0x7f7dffc162a0> Traceback (most recent call last): File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_subprocess.py", line 126, in __del__ self.close() File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_subprocess.py", line 104, in close proto.pipe.close() File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/unix_events.py", line 558, in close self._close(None) File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/unix_events.py", line 582, in _close self._loop.call_soon(self._call_connection_lost, exc) File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_events.py", line 761, in call_soon self._check_closed() File "/home/gert/.pyenv/versions/3.11.2/lib/python3.11/asyncio/base_events.py", line 519, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed DEBUG:asyncio:Using selector: EpollSelector Timeout on subprocess command, terminating. DEBUG:asyncio:Using selector: EpollSelector Timeout on subprocess command, terminating. DEBUG:asyncio:Using selector: EpollSelector Timeout on subprocess command, terminating. [...]
Either removing
proc.terminate()or replacing it withproc.kill()makes the traceback disappear. 🤷🏼Also notice the (unrelated) error:
WARNING:asyncio:Loop <_UnixSelectorEventLoop running=False closed=True debug=False> that handles pid 419211 is closedFurther relevant system info:
Python 3.11.2 (main, Mar 27 2023, 01:01:40) [GCC 12.2.1 20230201], built using Pyenv on Linux 6.2.9 (Arch, x86_64).Am I doing something wrong here or hitting a corner case that's not tackled by this bugfix? Thanks! 🙏🏼
You are missing
await proc.wait()after terminating the process, adding that fixes it. Anyways this is not related to this fix so create a new issue if you still face the issue.You are missing
await proc.wait()after terminating the process, adding that fixes it.Are you sure I should? It's also not included in Here’s an example of how asyncio can run a shell command and obtain its result, here: https://docs.python.org/3/library/asyncio-subprocess.html#
Note that I'm already awaiting the process result with
communicate(). Forwait()this comment (here) seems to indicate that as well I shouldn't:Use the communicate() method when using pipes to avoid this condition.
Anyways this is not related to this fix so create a new issue if you still face the issue.
Sorry for the noise then, but to me it sounds exactly the same still.
You are wrapping
proc.communicate()inwait_forso it gets cancelled before it had to chance to callself.wait(). See the source code. The docs are for directly awaiting theproc.communicate()which doesn't work in case ofwait_for.Reacted by Gert van Dijk- added 2 commits that reference this issue
on Dec 11, 2025
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields: