Skip to content

generated issue 14448 #8

@rigelbm

Description

@rigelbm

FastAPI incorrectly detects async/sync with functools.wraps decorators

Problem Description

When using functools.wraps or functools.partial on functions that serve as FastAPI dependencies or path operation functions (endpoints), FastAPI may incorrectly determine whether the function should be executed as synchronous or asynchronous. This results in runtime errors when the decorated functions are called.

Current Behavior

Starting in FastAPI version 0.123.5, decorated functions using functools.wraps cause execution errors. For example:

  1. A synchronous function is wrapped with an async decorator using @functools.wraps
  2. When the endpoint is called, FastAPI incorrectly treats the function as synchronous
  3. At runtime, this produces a ValueError about coroutine objects not being iterable
  4. Users receive "Internal Server Error" responses

Example scenario:

from functools import wraps
from fastapi import FastAPI

app = FastAPI()

def my_decorator(func):
    @wraps(func)
    async def wrapper():
        func()
        return 'OK'
    return wrapper

@app.get('/')
@my_decorator
def index():
    print('Hello!')

# Accessing this endpoint results in:
# ValueError: [TypeError("'coroutine' object is not iterable"), ...]

Scope of Issue

This problem affects:

  • Regular functions (both sync and async)
  • Generator functions (both sync and async)
  • Functions decorated with functools.wraps
  • Functions wrapped with functools.partial
  • Callable class instances (objects with __call__ methods)
  • Any combination of the above

The issue occurs when these decorated functions are used as:

  • Path operation functions (endpoint handlers)
  • Dependency injection functions

Expected Behavior

FastAPI should correctly identify whether a decorated function needs to be awaited, regardless of how many layers of decorators are applied. If either the original function OR any wrapper in the decorator chain is asynchronous, FastAPI should treat it as async and await it properly. The framework should handle all combinations of:

  • Sync functions with async wrappers
  • Async functions with sync wrappers
  • Functions using functools.wraps or functools.partial
  • Generator and async generator functions
  • Callable class instances with decorated __call__ methods

This functionality worked correctly in FastAPI versions prior to 0.123.5 and should be restored.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions