Skip to content

Conversation

@anton-ryzhov
Copy link

Hello everyone,

According to the documentation dependencies should be able to catch and handle (postprocess, ignore, overwrite, …) exceptions.

In fact that works for almost all cases, but not for pydantic validation errors for input parameters. It looks to me that the code block was just accidentally deindented and was placed after async with-block not inside.

First commit adds new tests where 2 of 6 are failing. The second fixes the issue.

@anton-ryzhov anton-ryzhov marked this pull request as ready for review October 22, 2024 19:43
@alejsdev alejsdev added the bug Something isn't working label Oct 24, 2024
@anton-ryzhov
Copy link
Author

Any feedback would be highly appreciated

@anton-ryzhov
Copy link
Author

A kind reminder 😉

@anton-ryzhov
Copy link
Author

@tiangolo could you please take a look?

@anton-ryzhov
Copy link
Author

I'd like to have this bug fixed. Is there a way I can help you with this PR?

@svlandeg svlandeg changed the title Dependencies catch validation errors 🐛 Ensure that dependencies can always catch validation errors Feb 24, 2025
@svlandeg
Copy link
Member

Hi @anton-ryzhov,

Thanks for your contribution! Definitely appreciate the detailed explanation and the various tests you've added to the PR.

We're currently working through a backlog of PRs and will put this in the queue to review. We'll get back to you once someone in the team has been able to review this in more detail. Thanks for your patience! 🙏

Copy link
Member

@YuriiMotov YuriiMotov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified test that shows the idea of this PR:

from fastapi import Depends, FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.testclient import TestClient

app = FastAPI()

errors = []

def dep_with_yield():
    try:
        yield "_"
    except RequestValidationError as e:
        print("Caught RequestValidationError")
        errors.append(e)
        raise

@app.get("/")
def get_(param: bool, _: str = Depends(dep_with_yield)):
    pass

client = TestClient(app)

def test_():
    errors.clear()
    client.get("/?param=invalid_value")
    assert len(errors) == 1  # assert 0 == 1

    assert isinstance(errors[0], RequestValidationError)

For now we can't catch RequestValidationError using try..except block inside the dependency with yield. This PR makes it possible.

I don't think that in current implementation "code block was just accidentally deindented and was placed after async with-block not inside" (<= quote from the initial comment). For me it looks like it was designed this way intentionally.
At the same time I couldn't find any downsides of this new behavior except the fact that it may brake the code of existing projects if they catch exceptions and rewrite them (in some cases it may lead to hiding request validation errors). But it's easily fixable.

UPD: here is the commit that introduced the if not errors: block

@anton-ryzhov
Copy link
Author

Simplified test that shows the idea of this PR:

Yeah, that's exactly the problem I'm trying to solve here.

I don't think that in current implementation "code block was just accidentally deindented and was placed after async with-block not inside" (<= quote from the initial comment). For me it looks like it was designed this way intentionally.

That was just my assumption. I think this is not very important now.

UPD: here is the commit that introduced the if not errors: block

This block was originally added here and RequestValidationError was raised within the async stack.

And this commit has put it out for some reason.

@anton-ryzhov
Copy link
Author

What's next? Are there any chances of this to be merged?

@svlandeg
Copy link
Member

@anton-ryzhov: we have put this on Sebastián's internal board to solicit feedback from him. Thanks for your patience 🙏

@github-actions github-actions bot added the conflicts Automatically generated when a PR has a merge conflict label Sep 29, 2025
@github-actions
Copy link
Contributor

This pull request has a merge conflict that needs to be resolved.

@anton-ryzhov
Copy link
Author

#14099 has fixed the original issue. Do you still want to merge new tests for that case?

@tiangolo
Copy link
Member

tiangolo commented Dec 2, 2025

Thanks for the effort @anton-ryzhov! 🍰

If the original issue is now solved, then I think we can close this one. Thanks for reporting back that as well! ☕

@tiangolo tiangolo closed this Dec 2, 2025
@anton-ryzhov
Copy link
Author

I've re-tested it again and noticed an issue with the current implementation. If a handler returns success value, a dependency can't overwrite it with an Exception / HTTPException.

This returns 200:

from fastapi import Depends, FastAPI, HTTPException

async def dependency():
    try:
        yield
    finally:
        raise HTTPException(status_code=400, detail="Dependency raises")

app = FastAPI(dependencies=[Depends(dependency)])

@app.get("/")
async def root():
    return None

and logs that error:

  File "fastapi/fastapi/routing.py", line 115, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "fastapi/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py", line 56, in wrapped_app
    raise RuntimeError("Caught handled exception, but response already started.") from exc
RuntimeError: Caught handled exception, but response already started.

If a handler raises HTTPException, another Exception or has an input validation error — then the dependency can catch and handle/overwrite the error successfully

@anton-ryzhov
Copy link
Author

This breaks scenario when a dependency manages DB session and commits transaction after all. Currently an error on such commit will be ignored and false successful result will be returned

@tiangolo
Copy link
Member

tiangolo commented Jan 5, 2026

Please check the new docs for dependencies with scope: https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#early-exit-and-scope

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants