tiangolo/fastapi · critical · FastAPIError
Response not awaited. There's a high chance that the…
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
FastAPIError (RuntimeError) raised in request_response's inner app (routing.py:149). The handler sets response_awaited = False, awaits the endpoint inside an AsyncExitStack, then sets response_awaited = True only after `await response(scope, receive, send)` runs. If a yielding dependency catches the endpoint's exception with a bare `except:` or `except Exception:` and does not re-raise, the exception is swallowed, the response is never sent, response_awaited stays False, and FastAPI raises this error to signal that the response was silently lost.
Solutions
- Re-raise in the except block of the yielding dependency: `except Exception: ...; raise`.
- Avoid bare `except:` / `except Exception:` around the yield; only handle specific exceptions you intend to suppress.
- Log inside the except but always re-raise so FastAPI can produce the error response.
- Reproduce with the app in question and check every dependency-with-yield for a swallowing except block.
Example fix
// before
def get_session():
session = Session(engine)
try:
yield session
except Exception:
pass # swallows the endpoint error -> FastAPIError: Response not awaited
finally:
session.close()
// after
def get_session():
session = Session(engine)
try:
yield session
except Exception:
logger.exception("request failed")
raise # let FastAPI handle the error response
finally:
session.close() Defensive patterns
Strategy: try-catch
Validate before calling
# Static check: flag yielding dependencies that swallow exceptions
import ast, pathlib
class YieldExceptChecker(ast.NodeVisitor):
def __init__(self, path):
self.path = path; self.issues = []
def visit_FunctionDef(self, node):
has_yield = any(isinstance(n, ast.Yield) for n in ast.walk(node))
if has_yield:
for n in ast.walk(node):
if isinstance(n, ast.ExceptHandler):
t = n.type
bare = t is None
broad = isinstance(t, ast.Name) and t.id == "Exception"
has_raise = any(isinstance(c, ast.Raise) for c in ast.walk(n))
if (bare or broad) and not has_raise:
self.issues.append((self.path, n.lineno))
self.generic_visit(node)
for p in pathlib.Path('.').rglob('*.py'):
YieldExceptChecker(str(p)).visit(ast.parse(p.read_text())) Try / catch
# In the yielding dependency itself
import logging
log = logging.getLogger(__name__)
def get_db():
db = Session()
try:
yield db
except Exception:
log.exception("error during request")
raise # MUST re-raise so FastAPI can build the error response
finally:
db.close() Prevention
- Always re-raise inside except blocks of a yielding dependency.
- Never use bare `except:` or `except Exception:` without `raise` around a yield.
- Catch only the specific exceptions you intend to suppress.
- Add an integration test that forces an endpoint error to confirm a real 5xx comes back.
When it happens
Trigger: A dependency defined with `yield` wraps its yield in try/except that catches the exception raised by the endpoint (bare except or except Exception) and does not re-raise it. The dependency's teardown runs, swallows the error, and the response coroutine never executes.
Common situations: Adding try/except around `yield` in a DB-session or resource dependency to 'log and continue'; catching Exception to suppress errors; upgrading an app where a dependency silently absorbed errors that FastAPI now detects.
Related errors
- validation error(s)
- No route exists for name
- Prefix and path cannot be both empty
- A frontend path cannot be empty
- A frontend path must start with '/'
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/054b6a3a7e4f2c4d.
Report an issue: GitHub.
Appendix: 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 3e8d1526d8)