Repository: mealie-recipes/mealie (python)

Issue:
[BUG] - 422 Unprocessable Entity when trying to save a Recipe

### First Check

- [x] This is not a feature request.
- [x] I added a very descriptive title to this issue (title field is above this).
- [x] I used the GitHub search to find a similar issue and didn't find it.
- [x] I searched the Mealie documentation, with the integrated search.
- [x] I already read the docs and didn't find an answer.
- [x] This issue can be replicated on the demo site (https://demo.mealie.io/).

### What is the issue you are experiencing?


Well, i get these logfile entriy when I am trying to save a recipe.
At first i did not realize what exactly caused the issue but after a bit of research i found out that when creating a new recipie the input fields for servings and yield are 0 as default. when these fields are NULL (empty) you cannot save the recipe but you dont get an info why.

### Steps to Reproduce

Create or edit a recipe and empty input fields servings and yield. then hit save.

### Please provide relevant logs

INFO     2026-01-05T17:07:19 - [2003:c2:c707:ab00:5562:8f2b:8ea7:1eab:0] 422 Unprocessable Entity "PUT /api/recipes/

### Mealie Version

Version
[v3.9.2](https://github.com/mealie-recipes/mealie/releases/tag/v3.9.2)

Build
[3be7056f2c2628f98f372ed4f8ae077a2be24ef2](https://github.com/mealie-recipes/mealie/commit/3be7056f2c2628f98f372ed4f8ae077a2be24ef2)

image: ghcr.io/mealie-recipes/mealie:latest

### Deployment

Docker (Linux)

### Additional Deployment Details

_No response_


Patch:
diff --git a/mealie/schema/recipe/recipe.py b/mealie/schema/recipe/recipe.py
index c398182daa0..a41d505ac57 100644
--- a/mealie/schema/recipe/recipe.py
+++ b/mealie/schema/recipe/recipe.py
@@ -148,6 +148,10 @@ class RecipeSummary(MealieModel):
     last_made: datetime.datetime | None = None
     model_config = ConfigDict(from_attributes=True)
 
+    @field_validator("recipe_servings", "recipe_yield_quantity", mode="before")
+    def clean_numbers(val: Any):
+        return val or 0
+
     @field_validator("recipe_yield", "total_time", "prep_time", "cook_time", "perform_time", mode="before")
     def clean_strings(val: Any):
         if val is None:


Test patch:
diff --git a/tests/unit_tests/schema_tests/test_recipe.py b/tests/unit_tests/schema_tests/test_recipe.py
new file mode 100644
index 00000000000..fd4ba0ba4e0
--- /dev/null
+++ b/tests/unit_tests/schema_tests/test_recipe.py
@@ -0,0 +1,63 @@
+from typing import Any
+from uuid import uuid4
+
+import pytest
+
+from mealie.schema.recipe import RecipeSummary
+
+SHOULD_ERROR = "this_test_should_error"
+
+
+@pytest.mark.parametrize("field", ["recipe_servings", "recipe_yield_quantity"])
+@pytest.mark.parametrize(
+    ["val", "expected"],
+    [
+        (0, 0),
+        (None, 0),
+        ("", 0),
+        (10, 10),
+        (2.25, 2.25),
+        ("10", 10),
+        ("invalid", SHOULD_ERROR),
+    ],
+)
+def test_recipe_number_sanitation(field: str, val: Any, expected: Any):
+    try:
+        recipe = RecipeSummary(
+            id=uuid4(),
+            user_id=uuid4(),
+            household_id=uuid4(),
+            group_id=uuid4(),
+            **{field: val},
+        )
+    except ValueError:
+        if expected == SHOULD_ERROR:
+            return
+        else:
+            raise
+
+    assert expected != SHOULD_ERROR, "Value should have errored"
+    assert getattr(recipe, field) == expected
+
+
+@pytest.mark.parametrize("field", ["recipe_yield", "total_time", "prep_time", "cook_time", "perform_time"])
+@pytest.mark.parametrize(
+    ["val", "expected"],
+    [
+        ("normal string", "normal string"),
+        ("", ""),
+        (None, None),
+        (10, "10"),
+        (2.25, "2.25"),
+    ],
+)
+def test_recipe_string_sanitation(field: str, val: Any, expected: Any):
+    recipe = RecipeSummary(
+        id=uuid4(),
+        user_id=uuid4(),
+        household_id=uuid4(),
+        group_id=uuid4(),
+        **{field: val},
+    )
+
+    assert getattr(recipe, field) == expected