tiangolo/fastapi · error · FastAPIError

Response not awaited. There's a high chance that the applica

Error message

Response not awaited. There's a high chance that the application code is raising an exception and a dependency with yield has a block with a bare except, or a block with except Exception, and is not raising the exception again. Read more about it in the docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-except

What it means

Raised by FastAPI's request handler after the dependency AsyncExitStack exits: a `response_awaited` flag is set True only once `await response(scope, receive, send)` completes (routing.py:144-147). If the path operation raised, line 144 raises and the flag stays False; normally that exception propagates out and this branch is never reached. It is reached only when a dependency-with-yield has a `bare except` / `except Exception` that swallows the exception instead of re-raising, so AsyncExitStack.throw unwinds 'cleanly' and control falls through to the `if not response_awaited` guard at routing.py:148-155. In short: your path operation threw, a yielding dependency ate the error, and no response was ever sent.

Source

Thrown at fastapi/routing.py:149

        else functools.partial(run_in_threadpool, func)  # type: ignore[call-arg]
    )  # ty: ignore[invalid-assignment]

    async def app(scope: Scope, receive: Receive, send: Send) -> None:
        request = Request(scope, receive, send)

        async def app(scope: Scope, receive: Receive, send: Send) -> None:
            # Starts customization
            response_awaited = False
            async with AsyncExitStack() as request_stack:
                scope["fastapi_inner_astack"] = request_stack
                async with AsyncExitStack() as function_stack:
                    scope["fastapi_function_astack"] = function_stack
                    response = await f(request)
                await response(scope, receive, send)
                # Continues customization
                response_awaited = True
            if not response_awaited:
                raise FastAPIError(
                    "Response not awaited. There's a high chance that the "
                    "application code is raising an exception and a dependency with yield "
                    "has a block with a bare except, or a block with except Exception, "
                    "and is not raising the exception again. Read more about it in the "
                    "docs: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-except"
                )

        # Same as in Starlette
        await wrap_app_handling_exceptions(app, request)(scope, receive, send)

    return app


# Copy of starlette.routing.websocket_session modified to include the
# dependencies' AsyncExitStack
def websocket_session(
    func: Callable[[WebSocket], Awaitable[None]],
) -> ASGIApp:

View on GitHub (pinned to 42a41db11f)

Solutions

  1. In every `yield` dependency, re-raise inside `except` blocks: `except Exception: ...; raise`.
  2. Prefer `try / finally` for teardown so cleanup cannot accidentally swallow the propagating exception.
  3. Narrow `except` to specific exception types instead of bare `except` or `except Exception`.
  4. Add a CI/lint check that flags `yield` functions containing `except` without a following `raise`.
  5. Reproduce with the failing endpoint and step through the dependency teardown to find the swallowing `except`.

Example fix

# before
def get_db():
    db = SessionLocal()
    try:
        yield db
    except Exception:
        logging.exception("ignored")  # swallows the request error
    finally:
        db.close()

# after
def get_db():
    db = SessionLocal()
    try:
        yield db
    except Exception:
        logging.exception("will re-raise")
        raise
    finally:
        db.close()
Defensive patterns

Strategy: validation

Validate before calling

import ast, inspect, textwrap

def yield_deps_swallow_exceptions(func) -> list[str]:
    """Best-effort static check: flag yield-funcs whose except blocks lack raise."""
    src = textwrap.dedent(inspect.getsource(func))
    tree = ast.parse(src)
    problems = []
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and any(
            isinstance(n, ast.Yield) for n in ast.walk(node)
        ):
        for tryn in [n for n in ast.walk(node) if isinstance(n, ast.Try)]:
            for handler in tryn.handlers:
                has_raise = any(isinstance(n, ast.Raise) for n in ast.walk(handler))
                bare = handler.type is None
                broad = isinstance(handler.type, ast.Name) and handler.type.id == "Exception"
                if (bare or broad) and not has_raise:
                    problems.append(f"{func.__name__}:{handler.lineno} swallows without raise")
    return problems

# usage at startup / in tests
# assert yield_deps_swallow_exceptions(get_db) == []

Try / catch

# The response was never sent, so the connection is already broken.
# Catch only to log and ensure the process stays healthy; you cannot
# recover the response.
from fastapi import FastAPI
from fastapi.exceptions import FastAPIError

app = FastAPI()

@app.exception_handler(FastAPIError)
async def _handle(request, exc):
    logging.critical("FastAPI routing error: %s", exc)
    # surface a clean 500; the socket may already be closed
    from fastapi.responses import JSONResponse
    return JSONResponse({"detail": "internal error"}, status_code=500)

Prevention

When it happens

Trigger: A `yield`-based dependency (e.g. `def get_db(): try: yield db except Exception: log(...)` without `raise`) combined with a path operation or nested dependency that raises during the request. Also triggered when the dependency's teardown uses `except` to swallow the GeneratorExit/exception injected by the AsyncExitStack on unwind.

Common situations: Session/transaction dependencies that log-and-swallow DB errors; porting sync `yield` generators that used broad `except`; cleanup code written as `try/except` instead of `try/finally`; a sub-dependency raising while a parent yield-dep catches everything 'to keep the app alive'.

Related errors


AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04). Data as JSON: /data/errors/054b6a3a7e4f2c4d.json. Report an issue: GitHub.