You will get a RuntimeError exception if you attempt to execute the same coroutine object more than once.
The message of the exception will be “cannot reuse already awaited coroutine“. The RuntimeError exception occurs because we cannot execute the same coroutine object more than once, meaning we cannot await it more than once or schedule it as an asyncio.Task more than once.
Instead, we should create one coroutine object instance and run that each time we require a coroutine to be executed.
In this tutorial, you will discover why we get the RuntimeError “cannot reuse already awaited coroutine” and how to fix it.
Let’s get started.
RuntimeError: cannot reuse already awaited coroutine
It is common to get a RuntimeError exception with the message:
- RuntimeError: cannot reuse already awaited coroutine
Developers often get this exception when first getting started with asyncio.
What does this RuntimeError exception mean?
How can we fix it and avoid it in the future?
Run loops using all CPUs, download your FREE book to learn how.
Why Do We Get RuntimeError: cannot reuse already awaited coroutine?
The RuntimeError exception with the message “cannot reuse already awaited coroutine” is caused because we attempt to run the same coroutine object more than once.
Recall that when we call a coroutine, e.g. custom_coro(), that it creates a coroutine object.
We can then run the coroutine object by awaiting it or by scheduling it as a task.
For example:
1 2 3 4 5 |
... # create a coroutine coro = custom_coro() # run the coroutine await coro |
Or:
1 2 3 4 5 |
... # create a coroutine coro = custom_coro() # schedule coroutine for execution later asyncio.create_task(coro) |
If you are new to coroutine objects, you can learn more in the tutorial:
If you are new to asyncio.Task objects, you can learn more in the tutorial:
If we attempt to run the same coroutine object more than once it will raise an exception:
- RuntimeError: cannot reuse already awaited coroutine
For example:
1 2 3 4 5 6 7 |
... # create a coroutine coro = custom_coro() # run coroutine await coro # run coroutine again await coro |
Or:
1 2 3 4 5 6 7 |
... # create a coroutine coro = custom_coro() # schedule coroutine for execution later asyncio.create_task(coro) # schedule coroutine again for execution later asyncio.create_task(coro) |
We cannot execute the same coroutine object more than once.
How to Avoid RuntimeError: cannot reuse already awaited coroutine
We can avoid the RuntimeError by creating coroutines and running them directly in one line.
For example:
1 2 3 |
... # create and await coroutine await custom_coro() |
Or
1 2 3 |
... # create and schedule coroutine for execution later asyncio.create_task(custom_coro()) |
Can We Run An asyncio.Task More Than Once?
No.
A task can only be executed once. If we attempt to execute it again, it will return immediately as the asyncio event loop will correctly determine that the task is already “done”.
We can create an asyncio task and attempt to run it more than once.
For example:
1 2 3 4 5 6 7 8 9 |
... # create a coroutine coro = custom_coro() # schedule coroutine for execution later task = asyncio.create_task(coro) # run the task await task # run the task again await task |
This will not result in a RuntimeError exception because we are not running the same coroutine object more than once.
Instead, the first await will suspend the caller and execute the task until completion. The second await will return immediately because the task is already done.
How to Run a Coroutine More Than Once
We can execute a coroutine more than once.
Instead of awaiting the same coroutine object more than once, we can create one coroutine object for each execution of the coroutine we require.
For example:
1 2 3 4 5 |
... # create and await a coroutine await custom_coro() # create and await a coroutine again await custom_coro() |
Or:
1 2 3 4 5 |
... # create and schedule coroutine for execution later asyncio.create_task(custom_coro()) # create and schedule coroutine again for execution later asyncio.create_task(custom_coro()) |
This allows a given coroutine to be executed as many times as we require.
Now that we know about the RuntimeError exception when executing the same coroutine object more than once and how to avoid it, let’s look at some worked examples.
Free Python Asyncio Course
Download your FREE Asyncio PDF cheat sheet and get BONUS access to my free 7-day crash course on the Asyncio API.
Discover how to use the Python asyncio module including how to define, create, and run new coroutines and how to use non-blocking I/O.
Example of Awaiting the Same Coroutine More Than Once
We can explore an example of executing a coroutine object more than once that results in a RuntimeError exception.
In this case, we can define a simple custom coroutine that reports a message and sleeps for one second. We can then create an instance of the coroutine and attempt to run it, then attempt to run it again. We expect a RuntimeError exception.
Firstly, we can define a custom coroutine that prints a message and sleeps for a moment.
1 2 3 4 5 6 |
# our work coroutine async def work(): # report a message print('work() is working!') # sleep a moment await asyncio.sleep(1) |
Next, in the main coroutine, we can report a message, and then create an instance of the work() coroutine. We can then run the instance by await it directly. We will then await the same instance again and report a final message.
1 2 3 4 5 6 7 8 9 10 11 12 |
# define coroutine async def main(): # entry point of the program print('Main is running') # create a coroutine object coro = work() # await the coro await coro # await the coro again await coro # report final message print('Main is done') |
Finally, we can start the event loop.
1 2 3 |
... # run the coroutine asyncio.run(main()) |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# SuperFastPython.com # example of reusing a coroutine import asyncio # our work coroutine async def work(): # report a message print('work() is working!') # sleep a moment await asyncio.sleep(1) # define coroutine async def main(): # entry point of the program print('Main is running') # create a coroutine object coro = work() # await the coro await coro # await the coro again await coro # report final message print('Main is done') # run the coroutine asyncio.run(main()) |
Running the example first starts the asyncio event loop and runs the main() coroutine.
The main() coroutine runs and reports a message.
It then creates an instance of the work() coroutine.
The main() coroutine then suspends and awaits the instance of the work() coroutine.
The work() coroutine runs, reports a message, and sleeps. It then resumes and terminates.
The main() coroutine then suspends again and awaits the same instance of the work() coroutine.
This fails and results in a RuntimeError exception with the message “cannot reuse already awaited coroutine“.
This highlights that we cannot execute the same coroutine object more than once.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Main is running work() is working! Traceback (most recent call last): File "...", line 26, in <module> asyncio.run(main()) File ".../asyncio/runners.py", line 190, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File ".../asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File ".../asyncio/base_events.py", line 653, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "...", line 21, in main await coro RuntimeError: cannot reuse already awaited coroutine |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of Reusing the Same Coroutine in a Task More Than Once
We can explore an example of attempting to execute the same coroutine object more than once by scheduling it as an asyncio.Task.
In this case, we can update the above example so that instead of awaiting the same instance of the coroutine object more than once, the main() coroutine creates and awaits two different asyncio.Task instances that both attempt to run the same coroutine object.
1 2 3 4 5 6 7 8 9 10 11 |
... # create a coroutine object coro = work() # wrap the coroutine in a task task = asyncio.create_task(coro) # await the task await task # wrap the coroutine in another task task2 = asyncio.create_task(coro) # await the task await task2 |
The updated main() coroutine with this change is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# define coroutine async def main(): # entry point of the program print('Main is running') # create a coroutine object coro = work() # wrap the coroutine in a task task = asyncio.create_task(coro) # await the task await task # wrap the coroutine in another task task2 = asyncio.create_task(coro) # await the task await task2 # report final message print('Main is done') |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# SuperFastPython.com # example of reusing a coroutine in a task import asyncio # our work coroutine async def work(): # report a message print('work() is working!') # sleep a moment await asyncio.sleep(1) # define coroutine async def main(): # entry point of the program print('Main is running') # create a coroutine object coro = work() # wrap the coroutine in a task task = asyncio.create_task(coro) # await the task await task # wrap the coroutine in another task task2 = asyncio.create_task(coro) # await the task await task2 # report final message print('Main is done') # run the coroutine asyncio.run(main()) |
Running the example first starts the asyncio event loop and runs the main() coroutine.
The main() coroutine runs and reports a message.
It then creates an instance of the work() coroutine.
The main() coroutine then creates a new asyncio.Task instance with the coroutine object. It suspends and awaits the new task.
The work() task runs, reports a message, and sleeps. It then resumes and terminates.
The main() coroutine then creates a second asyncio.Task instance that uses the same coroutine instance. It suspends again and awaits the second task instance.
This fails and results in a RuntimeError exception with the message “cannot reuse already awaited coroutine“.
This highlights that we cannot execute the same coroutine object when it is scheduled and executed as a background asyncio.Task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Main is running work() is working! Traceback (most recent call last): File "...", line 30, in <module> asyncio.run(main()) File ".../asyncio/runners.py", line 190, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File ".../asyncio/runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File ".../asyncio/base_events.py", line 653, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "...", line 25, in main await task2 RuntimeError: cannot reuse already awaited coroutine |
Example of Cannot Reuse asyncio.Task
We can explore an example of attempting to execute the same asyncio.Task instance more than once.
In this case, we will update the above example to create a single asyncio.Task instance from a single coroutine object then execute it more than once. We expect that the task will execute normally the first time and not execute the second time as it is already done.
We can update the main() coroutine to create the coroutine object and then create one asyncio.Task instance, then execute the same asyncio.Task instance more than once.
1 2 3 4 5 6 7 8 9 |
... # create a coroutine object coro = work() # wrap the coroutine in a task task = asyncio.create_task(coro) # await the task await task # await the task again await task |
The updated main() coroutine with these changes is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# define coroutine async def main(): # entry point of the program print('Main is running') # create a coroutine object coro = work() # wrap the coroutine in a task task = asyncio.create_task(coro) # await the task await task # await the task again await task # report final message print('Main is done') |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# SuperFastPython.com # example of reusing an asyncio task import asyncio # our work coroutine async def work(): # report a message print('work() is working!') # sleep a moment await asyncio.sleep(1) # define coroutine async def main(): # entry point of the program print('Main is running') # create a coroutine object coro = work() # wrap the coroutine in a task task = asyncio.create_task(coro) # await the task await task # await the task again await task # report final message print('Main is done') # run the coroutine asyncio.run(main()) |
Running the example first starts the asyncio event loop and runs the main() coroutine.
The main() coroutine runs and reports a message.
It then creates an instance of the work() coroutine.
Next, it creates an asyncio.Task instance using the work() coroutine object instance.
The main() coroutine then suspends and awaits the new task.
The work() task runs, reports a message, and sleeps. It then resumes and terminates.
The main() coroutine then suspends again and awaits the same task instance.
This returns immediately and does not execute the task again. The reason is because the asyncio.Task instance is already “done” and cannot execute again. It does not raise a RuntimeError exception.
This highlights that we cannot execute the same asyncio.Task object more than once.
1 2 3 |
Main is running work() is working! Main is done |
Example of Executing a Coroutine More Than Once
We can explore an example of executing the same coroutine more than once.
In this case, we will update the above example to create a new instance of the work() coroutine each time we wish to run it.
1 2 3 4 5 |
... # await the coro await work() # await the coro again, but it's a new instance await work() |
This will execute the coroutine each time and will not result in a RuntimeError exception.
The reason is that each time we run the work() coroutine we are executing a new and different coroutine object created from the coroutine function definition. Like creating and running two separate Python objects.
The updated main() coroutine with this change is listed below.
1 2 3 4 5 6 7 8 9 10 |
# define coroutine async def main(): # entry point of the program print('Main is running') # await the coro await work() # await the coro again, but it's a new instance await work() # report final message print('Main is done') |
Tying this together, the complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# SuperFastPython.com # example of executing a coroutine more than once import asyncio # our work coroutine async def work(): # report a message print('work() is working!') # sleep a moment await asyncio.sleep(1) # define coroutine async def main(): # entry point of the program print('Main is running') # await the coro await work() # await the coro again, but it's a new instance await work() # report final message print('Main is done') # run the coroutine asyncio.run(main()) |
Running the example first starts the asyncio event loop and runs the main() coroutine.
The main() coroutine runs and reports a message.
The main() coroutine creates a new work() coroutine instance then suspends and awaits the coroutine.
The work() coroutine runs, reports a message, and sleeps. It then resumes and terminates.
The main() coroutine then creates a second work() coroutine object and suspends again and awaits it.
The second work() coroutine runs, reports a message, and sleeps. It then resumes and terminates.
This highlights how we can run the same coroutine more than once by creating a new coroutine object instance each time.
1 2 3 4 |
Main is running work() is working! work() is working! Main is done |
Further Reading
This section provides additional resources that you may find helpful.
Python Asyncio Books
- Python Asyncio Mastery, Jason Brownlee (my book!)
- Python Asyncio Jump-Start, Jason Brownlee.
- Python Asyncio Interview Questions, Jason Brownlee.
- Asyncio Module API Cheat Sheet
I also recommend the following books:
- Python Concurrency with asyncio, Matthew Fowler, 2022.
- Using Asyncio in Python, Caleb Hattingh, 2020.
- asyncio Recipes, Mohamed Mustapha Tahrioui, 2019.
Guides
APIs
- asyncio — Asynchronous I/O
- Asyncio Coroutines and Tasks
- Asyncio Streams
- Asyncio Subprocesses
- Asyncio Queues
- Asyncio Synchronization Primitives
References
Takeaways
You now know why we get the RuntimeError “cannot reuse already awaited coroutine” and how to fix it.
Did I make a mistake? See a typo?
I’m a simple humble human. Correct me, please!
Do you have any additional tips?
I’d love to hear about them!
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Josh Rinard on Unsplash
Do you have any questions?