You can get an InvalidStateError exception when attempting to retrieve a return value result from an asyncio Task.
This will happen if we retrieve a result via the result() method on a task while the task is still running, e.g. the task is not yet done.
We can avoid the InvalidStateError exception by waiting for the task to be done before attempting to retrieve the result either directly or via a function such as asyncio.gather() or asyncio.wait().
In this tutorial, you will discover why we get the InvalidStateError: “Result is not set” and how to fix it.
Let’s get started.
InvalidStateError: Result is not set
It is common to get an InvalidStateError exception when using asyncio.
An InvalidStateError exception is raised with the message:
- InvalidStateError: Result is not set
Developers often get this exception when first getting started with asyncio.
What does this InvalidStateError 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 An InvalidStateError
We get an InvalidStateError exception when we try to retrieve a return value result from an asyncio.Task while the task is still running.
This will happen if we call the result() method on an asyncio.Task() instance and the task is not done.
For example:
1 2 3 |
... # attempt to get a result from the task value = task.result() |
This is the regular way to get a return value result from a task.
It will return the return value from the Task’s coroutine or re-raise an exception if the coroutine failed with an uncancelled exception.
The problem is, we must wait until the task is completed, e.g, has the state “done“. This means that the done() method on the task returns True.
You can learn more about retrieving results from tasks in the tutorial:
How to Avoid InvalidStateError
We can avoid an InvalidStateError when calling the result() method by waiting for the task to be done.
There are many ways we can do this, such as:
- Await the task directly
- Await the task via asyncio.gather()
- Await the task via asyncio.wait()
Let’s take a closer look at each in turn.
Await The Task Directly
The first approach is to await the task directly.
For example:
1 2 3 |
... # wait for the task to be done and get the result result = await task |
This does two things:
- Waits for the task to be done.
- Retrieves the return value from the task.
We would no longer need to call the result() method on the task. Nevertheless, we can call the result() method if we prefer.
For example:
1 2 3 4 5 |
... # wait for the task to be done await task # attempt to get a result from the task value = task.result() |
You can learn more about awaiting in the tutorial:
Await The Task Via asyncio.gather()
Another approach is to use the asyncio.gather() function.
This function takes one or more awaitables and returns once they are all done.
For example:
1 2 3 4 5 |
... # wait for the task to be done _ = await asyncio.gather(task) # attempt to get a result from the task value = task.result() |
The asyncio.gather() function itself will return a list of return values, so we may want to retrieve the return value from it directly.
For example:
1 2 3 4 5 |
... # wait for the task to be done results = await asyncio.gather(task) # get the result value = results[0] |
You can learn more about how to use the asyncio.gather() function in the tutorial:
Await The Task Via asyncio.wait()
Another similar approach is to use the asyncio.wait() function.
This function takes a collection of tasks and returns once a condition is met, where the default condition is that all provided tasks are done.
This means that we will have to enclose our single task in a list.
For example:
1 2 3 4 5 |
... # wait for the task to be done _ = await asyncio.wait([task]) # attempt to get a result from the task value = task.result() |
You can learn more about the asyncio.wait() function in the tutorial:
Now that we know how to avoid the InvalidStateError, 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 InvalidStateError: Result is not set
We can explore an example that raises an InvalidStateError.
In this case, we can define a custom coroutine that does some work and then returns a value. Our main program will schedule the coroutine as a background task, sleep a moment then attempt to retrieve and report the task result, which will fail with an InvalidStateError exception.
Firstly, we can define a custom coroutine that reports a message, sleeps for one second, and then returns a value.
The work() coroutine below implements this.
1 2 3 4 5 6 7 8 |
# custom coroutine async def work(): # report a message print('work() is working!') # sleep a moment to simulate work await asyncio.sleep(1) # return the result return 100 |
Next, we can define the main() coroutine.
A message is printed, then a work() coroutine is created and scheduled as a background task. The main() coroutine then sleeps for half a second, not long enough for the task to be completed.
It then retrieves and reports the result from the task before printing a final message.
The main() coroutine below implements this.
1 2 3 4 5 6 7 8 9 10 11 12 |
# main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait a moment await asyncio.sleep(0.5) # report the result print(f'Got: {task.result()}') # report final message print('Main is done') |
Finally, we can start the event loop.
1 2 3 |
... # start the event loop 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 27 28 |
# SuperFastPython.com # example of InvalidStateError: Result is not set import asyncio # custom coroutine async def work(): # report a message print('work() is working!') # sleep a moment to simulate work await asyncio.sleep(1) # return the result return 100 # main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait a moment await asyncio.sleep(0.5) # report the result print(f'Got: {task.result()}') # report final message print('Main is done') # start the event loop 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 the work() coroutine and schedules it as a background task. It then suspends and sleeps for half a second.
The work() task runs and reports a message before suspending and sleeping for one second.
The main() coroutine resumes and attempts to retrieve the result from the task.
This fails with an InvalidStateError exception and the message “Result is not set“.
This highlights that if we attempt to retrieve a result via the result() method before the task is done an InvalidStateError exception will be raised.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Main is running work() is working! Traceback (most recent call last): File "...", line 28, 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 23, in main print(f'Got: {task.result()}') ^^^^^^^^^^^^^ asyncio.exceptions.InvalidStateError: Result is not set. |
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of Avoiding InvalidStateError By Awaiting
We can explore how to avoid the InvalidStateError exception by awaiting the target task directly.
In this case, we can update the above example to await the task, and then retrieve the return value.
1 2 3 4 5 |
... # wait for the task to be done await task # report the result print(f'Got: {task.result()}') |
The updated main() coroutine with this change is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 |
# main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait for the task to be done await task # report the result print(f'Got: {task.result()}') # 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 avoiding InvalidStateError by awaiting import asyncio # custom coroutine async def work(): # report a message print('work() is working!') # sleep a moment to simulate work await asyncio.sleep(1) # return the result return 100 # main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait for the task to be done await task # report the result print(f'Got: {task.result()}') # report final message print('Main is done') # start the event loop 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 the work() coroutine and schedules it as a background task. It then suspends and awaits the task directly.
The work() task runs and reports a message before suspending and sleeping for one second.
The work() task then resumes and returns a value, terminating the task.
The main() coroutine resumes and retrieves the return value from the task and reports it before reporting a final message.
This highlights how we can avoid an InvalidStateError exception by awaiting the task directly.
1 2 3 4 |
Main is running work() is working! Got: 100 Main is done |
Example of Avoiding InvalidStateError By asyncio.gather()
We can explore how to avoid an InvalidStateError exception by awaiting the task with asyncio.gather().
In this case, we can update the example by awaiting a call to asyncio.gather() and passing the task instance as an argument.
1 2 3 |
... # wait for the task to be done await asyncio.gather(task) |
The updated main() coroutine with this change is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 |
# main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait for the task to be done await asyncio.gather(task) # report the result print(f'Got: {task.result()}') # 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 avoiding InvalidStateError by asyncio.gather() import asyncio # custom coroutine async def work(): # report a message print('work() is working!') # sleep a moment to simulate work await asyncio.sleep(1) # return the result return 100 # main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait for the task to be done await asyncio.gather(task) # report the result print(f'Got: {task.result()}') # report final message print('Main is done') # start the event loop 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 the work() coroutine and schedules it as a background task.
It then suspends and awaits the task in a call to asyncio.gather().
The work() task runs and reports a message before suspending and sleeping for one second.
The work() task then resumes and returns a value, terminating the task.
The main() coroutine resumes and retrieves and reports the return value from the task.
This highlights how we can avoid an InvalidStateError exception by awaiting the task with asyncio.gather().
1 2 3 4 |
Main is running work() is working! Got: 100 Main is done |
Example of Avoiding InvalidStateError By asyncio.wait()
We can explore how to avoid an InvalidStateError exception by awaiting the task with asyncio.wait().
In this case, we can update the example by awaiting a call to asyncio.wait() and passing the task instance as an argument wrapped in a new list object.
1 2 3 |
... # wait for the task to be done _ = await asyncio.wait([task]) |
The updated main() coroutine with this change is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 |
# main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait for the task to be done _ = await asyncio.wait([task]) # report the result print(f'Got: {task.result()}') # 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 avoiding InvalidStateError by asyncio.wait() import asyncio # custom coroutine async def work(): # report a message print('work() is working!') # sleep a moment to simulate work await asyncio.sleep(1) # return the result return 100 # main coroutine async def main(): # entry point of the program print('Main is running') # schedule as task task = asyncio.create_task(work()) # wait for the task to be done _ = await asyncio.wait([task]) # report the result print(f'Got: {task.result()}') # report final message print('Main is done') # start the event loop 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 the work() coroutine and schedules it as a background task.
It then suspends and awaits the task in a call to asyncio.wait() as a one-item list.
The work() task runs and reports a message before suspending and sleeping for one second.
The work() task then resumes and returns a value, terminating the task.
The main() coroutine resumes and retrieves and reports the return value from the task.
This highlights how we can avoid an InvalidStateError exception by awaiting the task within asyncio.wait().
1 2 3 4 |
Main is running work() is working! Got: 100 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 InvalidStateError: “Result is not set” 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 MOTIVID .ORG on Unsplash
Do you have any questions?