RuntimeError: Event Loop is Closed asyncio Fix

There are so many solutions to this Runtime error on Stack Overflow that don’t work. If you write Python code using asyncio, you either have or most likely will run into this runtime error at some point. I personally came across this error while running asynchronous API requests. When you’re running an async/await function using the asyncio library, you may get a RuntimeError: Event Loop is closed error even after your loop is already done running. This problem may not always affect the functionality of a single script but will affect the functionality of multiple scripts. 

In this post we’ll go over:

  • The asyncio run vs loop.run_until_complete commands
  • The Runtime Error: Event loop is closed Problem
    • Error logs from the asyncio library
  • The Runtime Error: Event loop is closed Solution
    • Editing the Source Code of the asyncio library locally.

Click here for the source code you can copy and paste directly into your program → asyncio Runtime Error: Event Loop is closed Solution

The asyncio Library and run vs loop.run_until_complete

The aforementioned issue can come up when using the asyncio.run() or the loop.run_until_complete() functions. The documentation suggests using run() over run_until_complete() because run() handles the setting and closing of the event loop object. The run() command is actually just a wrapper around the run_until_complete() command. For a more thorough explanation, I suggest reading this guide on run() vs run_until_complete().

Runtime Error: Event Loop is closed Problem

Usually, this error can be fixed by replacing the asyncio.run() command with the command asyncio.new_event_loop().run_until_complete(). However, when used in conjunction with the aiohttp library to make multiple API requests, that alternative will not work. This is due to multiple reasons. First, a TCP connector problem, second an SSL protocol problem, and thirdly an issue with the Proactor transport object. Let’s take a look at what the error logs for this problem may look like prior to the RuntimeError: Event Loop is closed line.

Error Logs from the asyncio Library

Here is what the logs look like for this error:

Unclosed connector
connections: ['[(<aiohttp.client_proto.ResponseHandler object at 0x00000254D4590280>, 25425.812)]']
connector: <aiohttp.connector.TCPConnector object at 0x00000254D4584730>
Fatal error on SSL transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x00000254D459E1C0>
transport: <_ProactorSocketTransport fd=916 read=<_OverlappedFuture cancelled>>
…
AttributeError: 'NoneType' object has no attribute 'send'
Exception ignored in: <function _SSLProtocolTransport.__del__ at 0x00000254D4095E50>
Traceback (most recent call last):
…
RuntimeError: Event loop is closed

Runtime Error: Event Loop is closed Solution

The problem with this error being raised isn’t so much that we can’t run the function. It’s more so we have an error exit code, can’t run multiple functions in sequence, and get yelled at by the command line. The asyncio library not the most stable. So, this error is not too surprising, but you know what is surprising? This error has been around for a while. I found so many solutions on Stack Overflow that don’t work. Theoretically run() should close the event loop gracefully. Gracefully means no errors. Let’s look at how we can change the source code to force a graceful shutdown of the function.

Edit the Source Code of the asyncio Library run Command Locally

How are we going to fix the error? We’re going to wrap a modification around the class for the _ProactorBasePipeTransport’s delete method. This is the method that shuts down the event loop throwing the error. To do this, we’re going to import the wraps annotation from functools and the _ProactorBasePipeTransport from asyncio.proactor_events. Technically we don’t have to import the Proactor class, but we’ll import for ease.

Let’s create our helper function to shut the interpreter up after we’ve already finished our loop. Our function will take one parameter, the function we’re wrapping. We’ll annotate a wrapper function that wraps the passed in function parameter. The inner wrapper function will take itself as an argument, and any number of unnamed and named arguments.

All the wrapper function does is try to execute the function as normal, except when there’s a RuntimeError, it’s not raised. After defining the functions, we’ll edit the __del__ function of the Proactor object and set it to the silenced version. Now the closing of the loop will not raise errors in the console.

"""fix yelling at me error"""
from functools import wraps
 
from asyncio.proactor_events import _ProactorBasePipeTransport
 
def silence_event_loop_closed(func):
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except RuntimeError as e:
            if str(e) != 'Event loop is closed':
                raise
    return wrapper
 
_ProactorBasePipeTransport.__del__ = silence_event_loop_closed(_ProactorBasePipeTransport.__del__)
"""fix yelling at me error end"""

Summary fixing the RuntimeError: Event Loop is closed asyncio Error

The problem we encountered was the program shutdown process raising an error when running asyncio.run() on an async event loop when it shouldn’t have. The solution we implemented doesn’t directly solve the issue of the program not closing all its processes gracefully, but it does protect us from the problem. We directly imported the responsible object and wrote a wrapper around it with the functools Python library. The wrapper function silences the RuntimeError.

Further Reading

I run this site to help you and others like you find cool projects and practice software skills. If this is helpful for you and you enjoy your ad free site, please help fund this site by donating below! If you can’t donate right now, please think of us next time.

Yujian Tang

5 thoughts on “RuntimeError: Event Loop is Closed asyncio Fix

Leave a Reply

Discover more from PythonAlgos

Subscribe now to keep reading and get access to the full archive.

Continue reading