From e831572c2dcfa14372b8e3892253a4af8ba7ec9f Mon Sep 17 00:00:00 2001 From: Precious Nwakama Date: Wed, 1 Jul 2026 19:06:03 +0800 Subject: [PATCH] fix: preserve empty form values for required params --- fastapi/dependencies/utils.py | 24 +++++++++++++----------- tests/test_form_default.py | 11 +++++++++++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 40dffba64..34e830b5f 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -762,22 +762,24 @@ def _get_multidict_value( value = values.getlist(alias) else: value = values.get(alias, None) - if ( - value is None - or ( - isinstance(field.field_info, params.Form) - and isinstance(value, str) # For type checks - and value == "" - ) - or ( - field_annotation_is_sequence(field.field_info.annotation) - and len(value) == 0 - ) + if value is None or ( + field_annotation_is_sequence(field.field_info.annotation) + and len(value) == 0 ): if field.field_info.is_required(): return else: return deepcopy(field.default) + + if ( + isinstance(field.field_info, params.Form) + and isinstance(value, str) # For type checks + and value == "" + ): + if field.field_info.is_required(): + return value + else: + return deepcopy(field.default) return value diff --git a/tests/test_form_default.py b/tests/test_form_default.py index c4d33e3fb..9186990ed 100644 --- a/tests/test_form_default.py +++ b/tests/test_form_default.py @@ -19,6 +19,11 @@ async def post_multi_part( return {"file": file, "age": age} +@app.post("/urlencoded-required") +async def post_url_encoded_required(name: Annotated[str, Form()]): + return name + + client = TestClient(app) @@ -32,3 +37,9 @@ def test_form_default_multi_part(): response = client.post("/multipart", data={"age": ""}) assert response.status_code == 200 assert response.json() == {"file": None, "age": None} + + +def test_required_form_field_with_empty_string(): + response = client.post("/urlencoded-required", data={"name": ""}) + assert response.status_code == 200 + assert response.text == ""