FastAPI "coroutine object is not iterable" error

David Y.

The Problem

After I add the async keyword to a FastAPI function, calling the associated route produces the following error:

Click to Copy
[TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]

Here’s a simplified version of my application code:

Click to Copy
from fastapi import FastAPI import asyncio import job # internal module app = FastAPI() @app.get("/run-job") @login_required async def run_job(): result = await job.run() return {"message": result }

I need to run the asynchronous method job.run in this endpoint’s code, so the function itself has to be asynchronous. What is causing this error and how can I fix it?

The Solution

The most likely cause of this error is the decorator @login_required, which we can assume returns a synchronous function. Even though run_job is asynchronous, the actual function that gets executed after the decorator is applied will be synchronous, leading to an error when await is called.

To fix this code, we will either need to alter login_required so that it returns an asynchronous function or create another decoration (e.g. login_required_async) that does so. For example:

Click to Copy
def login_required_async(f): async def wrapper(*args, **kwargs): # ... access control logic goes here ... return await f(*args, **kwargs) return wrapper

Depending on how the rest of our application functions, we may require both synchronous and asynchronous versions of @login_required. We can create one function for both using a conditional return that checks whether the passed-in function is synchronous or asynchronous:

Click to Copy
from inspect import iscoroutinefunction from functools import wraps def login_required(decorator_option=None): def access_control(*args, **kwargs): # ... access control logic goes here ... @wraps(f) def wrapper(*args, **kwargs): access_control(*args, **kwargs) return f(*args, **kwargs) @wraps(f) async def async_wrapper(*args, **kwargs): access_control(*args, **kwargs) return await f(*args, **kwargs) return async_wrapper if iscoroutinefunction(f) else wrapper

iscoroutinefunction from Python’s built-in inspect module will return True if the passed-in function is asynchronous and False if it is synchronous. This will allow us to use the @login_required decorator on synchronous and asynchronous functions alike.

Loved by over 4 million developers and more than 90,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.

Share on Twitter
Bookmark this page
Ask a questionJoin the discussion

Related Answers

A better experience for your users. An easier life for your developers.

    TwitterGitHubDribbbleLinkedinDiscord
© 2024 • Sentry is a registered Trademark
of Functional Software, Inc.