]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
📝 Update docs about re-raising validation errors, do not include string as is to...
authorSebastián Ramírez <tiangolo@gmail.com>
Wed, 10 Dec 2025 12:56:50 +0000 (04:56 -0800)
committerGitHub <noreply@github.com>
Wed, 10 Dec 2025 12:56:50 +0000 (12:56 +0000)
docs/en/docs/tutorial/handling-errors.md
docs_src/handling_errors/tutorial004.py
tests/test_tutorial/test_handling_errors/test_tutorial004.py

index 53501837c9e246df0c9beedb1b563634d26e829d..4092039b160dc3798bab28f50418ec9e6add1b1c 100644 (file)
@@ -127,7 +127,7 @@ To override it, import the `RequestValidationError` and use it with `@app.except
 
 The exception handler will receive a `Request` and the exception.
 
-{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:16] *}
+{* ../../docs_src/handling_errors/tutorial004.py hl[2,14:19] *}
 
 Now, if you go to `/items/foo`, instead of getting the default JSON error with:
 
@@ -149,36 +149,17 @@ Now, if you go to `/items/foo`, instead of getting the default JSON error with:
 you will get a text version, with:
 
 ```
-1 validation error
-path -> item_id
-  value is not a valid integer (type=type_error.integer)
+Validation errors:
+Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer
 ```
 
-#### `RequestValidationError` vs `ValidationError` { #requestvalidationerror-vs-validationerror }
-
-/// warning
-
-These are technical details that you might skip if it's not important for you now.
-
-///
-
-`RequestValidationError` is a sub-class of Pydantic's <a href="https://docs.pydantic.dev/latest/concepts/models/#error-handling" class="external-link" target="_blank">`ValidationError`</a>.
-
-**FastAPI** uses it so that, if you use a Pydantic model in `response_model`, and your data has an error, you will see the error in your log.
-
-But the client/user will not see it. Instead, the client will receive an "Internal Server Error" with an HTTP status code `500`.
-
-It should be this way because if you have a Pydantic `ValidationError` in your *response* or anywhere in your code (not in the client's *request*), it's actually a bug in your code.
-
-And while you fix it, your clients/users shouldn't have access to internal information about the error, as that could expose a security vulnerability.
-
 ### Override the `HTTPException` error handler { #override-the-httpexception-error-handler }
 
 The same way, you can override the `HTTPException` handler.
 
 For example, you could want to return a plain text response instead of JSON for these errors:
 
-{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,22] *}
+{* ../../docs_src/handling_errors/tutorial004.py hl[3:4,9:11,25] *}
 
 /// note | Technical Details
 
@@ -188,6 +169,14 @@ You could also use `from starlette.responses import PlainTextResponse`.
 
 ///
 
+/// warning
+
+Have in mind that the `RequestValidationError` contains the information of the file name and line where the validation error happens so that you can show it in your logs with the relevant information if you want to.
+
+But that means that if you just convert it to a string and return that information directly, you could be leaking a bit of information about your system, that's why here the code extracts and shows each error independently.
+
+///
+
 ### Use the `RequestValidationError` body { #use-the-requestvalidationerror-body }
 
 The `RequestValidationError` contains the `body` it received with invalid data.
index 300a3834ff47cc05997f78565eb48cba8758e09a..ae50807e973361c26b9ab53b8836326177b66023 100644 (file)
@@ -12,8 +12,11 @@ async def http_exception_handler(request, exc):
 
 
 @app.exception_handler(RequestValidationError)
-async def validation_exception_handler(request, exc):
-    return PlainTextResponse(str(exc), status_code=400)
+async def validation_exception_handler(request, exc: RequestValidationError):
+    message = "Validation errors:"
+    for error in exc.errors():
+        message += f"\nField: {error['loc']}, Error: {error['msg']}"
+    return PlainTextResponse(message, status_code=400)
 
 
 @app.get("/items/{item_id}")
index 217159a59685e9311f5b822598297668a3d6546a..c04bf372456ca0ac43a2c3297e65caf23724354e 100644 (file)
@@ -8,18 +8,8 @@ client = TestClient(app)
 def test_get_validation_error():
     response = client.get("/items/foo")
     assert response.status_code == 400, response.text
-    # TODO: remove when deprecating Pydantic v1
-    assert (
-        # TODO: remove when deprecating Pydantic v1
-        "path -> item_id" in response.text
-        or "'loc': ('path', 'item_id')" in response.text
-    )
-    assert (
-        # TODO: remove when deprecating Pydantic v1
-        "value is not a valid integer" in response.text
-        or "Input should be a valid integer, unable to parse string as an integer"
-        in response.text
-    )
+    assert "Validation errors:" in response.text
+    assert "Field: ('path', 'item_id')" in response.text
 
 
 def test_get_http_error():