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.

Exceptions with handlers are replaced in a background task

See original GitHub issue

When a background task raises an exception that has a FastAPI handler, that exception is replaced by FastAPI and the original error cannot be found in the server logs. This makes debugging and investigations incredibly hard as it’s impossible to know where the error occurred.

FastAPI should preserve the original exception so that it can be logged and investigated server-side.

Example

from fastapi import FastAPI, Request, BackgroundTasks
from fastapi.responses import JSONResponse


class UnicornException(Exception):
    def __init__(self, name: str):
        self.name = name


app = FastAPI()


@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
    return JSONResponse(
        status_code=418,
        content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
    )


async def process_unicorn(name: str):
    if name == "yolo":
        raise UnicornException(name=name)
    # else do_something(name)

@app.get("/unicorns/{name}")
async def sync_process_unicorn(name: str, background_tasks: BackgroundTasks):
    await process_unicorn(name)
    return name

@app.get("/unicorns_async/{name}")
async def async_process_unicorn(name: str, background_tasks: BackgroundTasks):
    """Accept the unicorn and process it in the background"""
    background_tasks.add_task(process_unicorn, name=name)
    return f"Processing {name} asynchronously."

Description

  • /unicorns/yolo correctly returns an error response
  • /unicorns_async/yolo returns the correct response to the client. I expect error logs and traceback of a UnicornException on the server. However, that exception is replaced with one that FastAPI generates, without context.
INFO:     127.0.0.1:32848 - "GET /unicorns_async/yolo HTTP/1.1" 200 OK
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/home/luyao/.local/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 394, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/luyao/.local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/home/luyao/.local/lib/python3.8/site-packages/fastapi/applications.py", line 190, in __call__
    await super().__call__(scope, receive, send)
  File "/home/luyao/.local/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/luyao/.local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/home/luyao/.local/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/home/luyao/.local/lib/python3.8/site-packages/starlette/exceptions.py", line 86, in __call__
    raise RuntimeError(msg) from exc
RuntimeError: Caught handled exception, but response already started.

Environment

  • OS: Linux (Ubuntu 20.10)
  • FastAPI Version: 0.62.0
  • Python version: occurs on both 3.8.6 and 3.7.9

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

5reactions
snazzyfoxcommented, Dec 11, 2020

Hmm, you’re right that it seems to be an issue with Starlette. I’ll open an issue there instead.

I don’t think this particular issue has to do with forgetting to await the result or threading though. The exception is clearly there - in fact, if I go to the code quoted in the traceback and insert print(exc) before the raise, it literally prints out the exact exception that the application has raised. So the original exception is available in the context; it’s just being replaced with a less helpful error instead of surfaced through the logs.

3reactions
johachicommented, Dec 2, 2021

@Matthieu-Tinycoaching

I solved this by using a message broker (e.g. Rabbit MQ) and not using BackGroundTasks. I needed a message broker for other reasons, so it was no extra work for me. However, that will probably be overkill to solve only this problem.

There is a good blog post here on how to mitigate this problem (I’m the author) as well as this library that can also solve this problem for you.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Managing Exceptions in BackgroundService or ... - Mike Conrad
.Net 6 introduces a welcome change to exceptions which is detailed ... In this post, we will cover proper exception handling for hosted ......
Read more >
How to Handle Exceptions from Background Worker ...
Exceptions are thrown on background worker thread's call stack and its own stack frames are unfolded.
Read more >
NET 6 breaking change: Exception handling in hosting - .NET
NET 6 breaking change in core .NET libraries where unhandled exceptions from a BackgroundService are logged instead of lost.
Read more >
How to add background tasks when request fails and ...
The way to do this is to override the HTTPException error handler, and since there is no BackgroundTasks object in the exception_handler ...
Read more >
Background to exception handling in SQL Server
However, the examples demonstrated in preceding sections should not justify an absolute replacement of any T-SQL code that references RAISERROR ...
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