RuntimeError: cannot reuse already awaited coroutine
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?
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:
...
# create a coroutine
coro = custom_coro()
# run the coroutine
await coro
Or:
...
# 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:
...
# create a coroutine
coro = custom_coro()
# run coroutine
await coro
# run coroutine again
await coro
Or:
...
# 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:
...
# create and await coroutine
await custom_coro()
Or
...
# 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:
...
# 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:
...
# create and await a coroutine
await custom_coro()
# create and await a coroutine again
await custom_coro()
Or:
...
# 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.
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.
# 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.
# 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.
...
# run the coroutine
asyncio.run(main())
Tying this together, the complete example is listed below.
# 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.
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
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.
...
# 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.
# 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.
# 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.
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.
...
# 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.
# 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.
# 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.
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.
...
# 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.
# 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.
# 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.
Main is running
work() is working!
work() is working!
Main is done
Takeaways
You now know why we get the RuntimeError "cannot reuse already awaited coroutine" and how to fix it.
If you enjoyed this tutorial, you will love my book: Python Asyncio Jump-Start. It covers everything you need to master the topic with hands-on examples and clear explanations.