9

Why does adding async to Fastapi function gives me the "'coroutine' object is not iterable" error

I only get the error after I add the async keyword at the front of my function as follows, when I call the function/endpoint using Swagger UI:

@router.post("/create")
async def job_create_post_view(
    request: Request, 
    is_htmx=Depends(is_htmx), 
    db:Session=Depends(get_db),
    short_description: str = Form(default=None),
    long_description: str = Form(default=None),
   
   .....
    
    job_image:Optional[UploadFile]=File(...)
    ):
    

The error is as follows:

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

I am trying to do something asynchronous inside the function:

contents = await job_image.read()

This is the stack trace:

Traceback (most recent call last):
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\uvicorn\protocols\http\h11_impl.py", line 366, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__
    return await self.app(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\fastapi\applications.py", line 269, in __call__
    await super().__call__(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\middleware\errors.py", line 184, in __call__
    raise exc
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\middleware\errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\middleware\cors.py", line 92, in __call__
    await self.simple_response(scope, receive, send, request_headers=headers)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\middleware\cors.py", line 147, in simple_response
    await self.app(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\middleware\authentication.py", line 48, in __call__
    await self.app(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\exceptions.py", line 93, in __call__
    raise exc
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\exceptions.py", line 82, in __call__
    await self.app(scope, receive, sender)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 21, in __call__
    raise e
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\routing.py", line 670, in __call__
    await route.handle(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\routing.py", line 266, in handle
    await self.app(scope, receive, send)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\starlette\routing.py", line 65, in app
    response = await func(request)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\fastapi\routing.py", line 235, in app
    response_data = await serialize_response(
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\fastapi\routing.py", line 149, in serialize_response
    return jsonable_encoder(response_content)
  File "D:\TEMP\job_search - revert\venv\lib\site-packages\fastapi\encoders.py", line 144, in jsonable_encoder
    raise ValueError(errors)
ValueError: [TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
4
  • How are you calling the function? What is the @login_required decorator? What is the stack trace to where the error gets thrown? Commented Jun 14, 2022 at 6:45
  • You removed the login_required decorator from the source now, but that may very well be the cause of your error - are you still running the code with it? Do you have a small view function that shows the issue? Commented Jun 14, 2022 at 7:53
  • Yes it worked after I removed the decorator. Am I not allowed to use a decorator with async functions? Commented Jun 14, 2022 at 8:49
  • 2
    The function that you return from your decorator will need to be a async function as well in that case, so you have to at least make sure that everything gets handled correctly inside your decorator. However, usually you don't use decorators like that with FastAPI, but uses the Depends injection mechanism instead (also available as Security for things like handling the user being logged in, etc). Commented Jun 14, 2022 at 9:04

3 Answers 3

16

I had the same error.
I found out that the problem was from calling a async function without putting await before calling it.

The function which I was trying to call, was like something like this:

async def func1():
    ...

async def func2():
    x = func1()
    ...

So, the problem got resolved when I edited like this:

async def func1():
    ...

async def func2():
    x = await func1()
    ...
Sign up to request clarification or add additional context in comments.

Comments

6

[TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')] usually happened when the developer made a mistake inside the async function like this [Mistake]:

@app.get("/")
 async def read_root():
    return fetch_all_todos()

but the correct behavior for async is like this [Correct]:

@app.get("/")
async def read_root():
    response = await fetch_all_todos()
    return response

You need to put await in a variable and then return it.

2 Comments

You can also write return await fetch_all_todos().
Correct! we can do it, but my version is better for debugging.
0

I faced the issue while using Celery with Redis in FastAPI. This was the code where the main problem raised:

@router.post('/bg_test')
def bg_test(text: str):
    return create_task.delay(text=text)

As the create_task is a celery task, I can't return the result in the main router, rather I need to initiate the run and handle the rest in celery and return the task id here. Here is the working version:

@router.post('/bg_test')
def bg_test(text: str):
    task = create_task.delay(text=text)
    return task.id

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.