question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[QUESTION] Is it possible to catch exceptions in async generator dependencies?

See original GitHub issue

Description

Is it possible for an async generator dependency (as seen in the docs) to handle exceptions thrown by an endpoint?

Additional context

Suppose I have the following get_db function:

async def get_db(request: Request) -> asyncpg.connection.Connection:
    """Obtain a database connection from the pool."""
    if pool is None:
        logger.error("Unable to provide a connection on an uninitalised pool.")
        raise HTTPException(
            status_code=HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Unable to provide a connection on an uninitalised pool.",
        )

    conn = None

    try:
        conn = await pool.acquire()

        # Test that the connection is still active by running a trivial query
        # (https://docs.sqlalchemy.org/en/13/core/pooling.html#disconnect-handling-pessimistic)
        try:
            await conn.execute("SELECT 1")
        except ConnectionDoesNotExistError:
            conn = await pool.acquire()

        yield conn
    except (PostgresConnectionError, OSError) as e:
        logger.error("Unable to connect to the database: %s", e)
        raise HTTPException(
            status_code=HTTP_500_INTERNAL_SERVER_ERROR, detail="Unable to connect to the database."
        )
    # --- The problem is with this particular exception handling ---
    except SyntaxOrAccessError as e:
        logger.error("Unable to execute query: %s", e)
        raise HTTPException(
            status_code=HTTP_500_INTERNAL_SERVER_ERROR,
            detail="Unable to execute the required query against the database.",
        )
    finally:
        if pool is not None and conn:
            await pool.release(conn)

If an endpoint using this dependency runs an invalid query, I would like to catch that exception and handle it here. However, it seems that due to the way FastAPI is wrapping the async context manager, this is not possible. Instead the exception is not caught by my dependency function, but instead thrown in the ASGI console.

The client just sees:

HTTP/1.1 500 Internal Server Error
content-length: 21
content-type: text/plain; charset=utf-8
date: Mon, 21 Oct 2019 21:24:53 GMT
server: uvicorn

Internal Server Error

This is normally possible using asynccontextmanager like so:

from async_generator import asynccontextmanager

@asynccontextmanager
async def boo():
    print("before")
    try:
        yield "hello"
    except ValueError as e:
        print("got an exception", e)
    print("after")


async with boo() as b:
    print("got", b)
    raise ValueError("oh no")

Output:

before
got hello
got an exception oh no
after

I suspect this relates to the following function and how it handles exceptions: https://github.com/tiangolo/fastapi/blob/master/fastapi/concurrency.py#L36

Thanks so much for your help in advance 😄 Fotis

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
tiangolocommented, Feb 10, 2020

@fgimian you can’t raise exceptions from dependencies after the yield. You can raise before the yield and it will be handled by the default exception handler (even before except code in your dependency is reached).

But the context of dependencies is before/around the context of exception handlers. It happens outside of the request handling.

So, you could even return a response, and then have a bunch of background tasks running using the same DB session from the dependency, and they could throw an error, and it would still be caught by the dependency with yield in the the except block. And that would be long after sending the response. Also, long after the last chance to raise an HTTPException, but you could still handle the error and close the DB session.

1reaction
dmontagucommented, Oct 21, 2019

Oh, I see what you mean, you want it to basically work as an exception handler. I think that might be hard to handle properly. The reason for that is that the dependency function isn’t executed in a parent scope of the endpoint function.

But it should at least currently behave properly in terms of cleanup.

I suspect there may be a way to make this work as is or with minor modifications to fastapi, but I think it might require some heavy digging into contextlib to figure it out.

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - Catch an exception thrown by an async void method
It's somewhat weird to read but yes, the exception will bubble up to the calling code - but only if you await or...
Read more >
Asynchronous iteration • Exploring ES2018 and ES2019
Sync iterables are automatically converted to async iterables, just like with for-await-of . 5.3.4. Errors. In normal generators, next() can throw exceptions.
Read more >
Asynchronous actions - MobX-state-tree
The recommended way to write asynchronous actions is by using flow and generators. They always return a promise, and work for all practical...
Read more >
Issue 38559: async generators aclose() behavior in 3.8
msg355158 ‑ (view) Author: Yury Selivanov (yselivanov) * Date: 2019‑10‑22 21:48 msg355160 ‑ (view) Author: Yury Selivanov (yselivanov) * Date: 2019‑10‑22 22:09 msg355163 ‑ (view)...
Read more >
TypeError: 'x' is not iterable - JavaScript - MDN Web Docs
The JavaScript exception "is not iterable" occurs when the value which is given as the right-hand side of for...of, as argument of a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found