mealie-recipes / mealie

Mealie is a self hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family. Easily add recipes into your database by providing the url and mealie will automatically import the relevant data or add a family recipe with the UI editor
https://docs.mealie.io
GNU Affero General Public License v3.0
7.14k stars 716 forks source link

[BUG] - 500 error on PUT method for updating Recipe #4401

Open sciencetor2 opened 3 days ago

sciencetor2 commented 3 days ago

First Check

What is the issue you are experiencing?

when submitting a PUT to the recipe API, i experienced a 500

Steps to Reproduce

submit the following json object via PUT:

{'recipeIngredient' : [{'quantity': 2.0, 'unit': {'id': 'dae51755-cc6b-4320-ba52-ca722556ff30', 'name': 'pound', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': 'lb', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.165900Z', 'updateAt': '2024-08-13T02:54:16.165903Z'}, 'food': {'id': '39691316-7658-4e1a-9754-d9f5de2983b9', 'name': 'ground beef', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T21:41:05.660692Z', 'updateAt': '2024-10-12T21:41:05.660696Z'}, 'note': 'I used 96/4 from HEB', 'isFood': False, 'disableAmount': False, 'display': '2 I used 96/4 from HEB', 'title': None, 'originalText': '2 lbs Ground Beef, I used 96/4 from HEB', 'referenceId': 'cb68eedc-eb18-43a8-ba68-e809f9ee2630'}, {'quantity': 6.0, 'unit': {'id': '8dac357e-af3d-4abc-a790-f26c01e59987', 'name': 'slice', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': '', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-10-12T22:56:53.035618Z', 'updateAt': '2024-10-12T22:56:53.035620Z'}, 'food': {'id': 'e3c458bd-22fc-4f3f-b5ac-627eb20f1f8c', 'name': 'center cut bacon', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:46:41.592133Z', 'updateAt': '2024-10-12T22:46:41.592137Z'}, 'note': 'cut into thin strips', 'isFood': False, 'disableAmount': False, 'display': '6 cut into thin strips', 'title': None, 'originalText': '6 slices Center Cut Bacon, cut into thin strips', 'referenceId': 'f9ba34ff-0463-485b-ace3-94f60bce8dec'}, {'quantity': 1.0, 'unit': {'id': 'a764e229-8c7b-473c-8f66-f0bff6a5fe71', 'name': 'medium', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': '', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-10-12T22:58:00.380419Z', 'updateAt': '2024-10-12T22:58:00.380423Z'}, 'food': {'id': '730939f5-c207-43ca-8bd0-992462450bf3', 'name': 'sweet onion', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:57:03.234865Z', 'updateAt': '2024-10-12T22:57:03.234870Z'}, 'note': '200g, diced', 'isFood': False, 'disableAmount': False, 'display': '200g, diced', 'title': None, 'originalText': '1 medium (200g) Sweet Onion, diced', 'referenceId': 'ff044c4a-45be-4627-b6ba-e05ea75e73ad'}, {'quantity': 2.0, 'unit': {'id': 'a764e229-8c7b-473c-8f66-f0bff6a5fe71', 'name': 'medium', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': '', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-10-12T22:58:00.380419Z', 'updateAt': '2024-10-12T22:58:00.380423Z'}, 'food': {'id': '54223689-53c3-475c-b83c-b6649bb286a8', 'name': 'carrot', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-08-13T02:54:18.456017Z', 'updateAt': '2024-08-13T02:54:18.456020Z'}, 'note': '150g, peeled and finely grated', 'isFood': False, 'disableAmount': False, 'display': '2 150g, peeled and finely grated', 'title': None, 'originalText': '2 medium (150g) Carrots, peeled and finely grated', 'referenceId': 'a610fe15-5cd1-4051-95b9-99c2c82e05bf'}, {'quantity': 4.0, 'unit': {'id': 'e1d2bf6c-4f0b-4536-ae5b-902f50668fa6', 'name': 'clove', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': '', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.807216Z', 'updateAt': '2024-08-13T02:54:16.807219Z'}, 'food': {'id': '122bf715-3d33-4120-b94d-62a623c8d351', 'name': 'garlic', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-08-13T02:54:18.028625Z', 'updateAt': '2024-08-13T02:54:18.028628Z'}, 'note': 'minced', 'isFood': False, 'disableAmount': False, 'display': '4 minced', 'title': None, 'originalText': '4 cloves (20g) Garlic, minced', 'referenceId': '0feca850-b3e9-49f2-a528-c19f4916d640'}, {'quantity': 2.0, 'unit': {'id': 'a1b1c031-8017-4248-af3c-b80475675846', 'name': 'tablespoon', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': 'tbsp', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.068349Z', 'updateAt': '2024-08-13T02:54:16.068352Z'}, 'food': {'id': '54c6fde2-b800-4d70-aee7-9812fe3f90b0', 'name': 'red wine vinegar', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:58:08.588407Z', 'updateAt': '2024-10-12T22:58:08.588411Z'}, 'note': '30g', 'isFood': False, 'disableAmount': False, 'display': '2 30g', 'title': None, 'originalText': '2 Tbsp (30g) Red Wine Vinegar', 'referenceId': 'fd002f96-491f-4b88-bc8a-324341ea6ef3'}, {'quantity': 28.0, 'unit': {'id': 'e05e5d38-8802-48d1-a93e-c843a093c142', 'name': 'ounce', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': 'oz', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.184855Z', 'updateAt': '2024-08-13T02:54:16.184858Z'}, 'food': {'id': 'c06b2ead-75bb-4201-9014-5cfe607a6188', 'name': 'crushed tomatoes', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:58:11.687759Z', 'updateAt': '2024-10-12T22:58:11.687764Z'}, 'note': None, 'isFood': False, 'disableAmount': False, 'display': '28', 'title': None, 'originalText': '28 oz can Crushed Tomatoes', 'referenceId': 'd2b2287a-3d9e-4f97-9adb-20fefa94fce7'}, {'quantity': 0.5, 'unit': {'id': 'a1b1c031-8017-4248-af3c-b80475675846', 'name': 'tablespoon', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': 'tbsp', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.068349Z', 'updateAt': '2024-08-13T02:54:16.068352Z'}, 'food': {'id': '45c36874-03e1-459c-9dda-270619ad35cb', 'name': 'dried oregano', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T21:42:51.245431Z', 'updateAt': '2024-10-12T21:42:51.245433Z'}, 'note': None, 'isFood': False, 'disableAmount': False, 'display': '¹/₂', 'title': None, 'originalText': '1/2 Tbsp Dried Oregano', 'referenceId': '72c7a157-822e-4813-bbe4-65c3256b86af'}, {'quantity': 1.0, 'unit': {'id': '1047fd1c-d994-481f-a210-c81fefb30953', 'name': 'teaspoon', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': 'tsp', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.036386Z', 'updateAt': '2024-08-13T02:54:16.036390Z'}, 'food': {'id': '6d09cbcb-ecce-457e-a87b-aff01b0eb833', 'name': 'crushed red pepper', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:58:15.404246Z', 'updateAt': '2024-10-12T22:58:15.404251Z'}, 'note': None, 'isFood': False, 'disableAmount': False, 'display': '', 'title': None, 'originalText': '1 tsp Crushed Red Pepper', 'referenceId': '6b845449-70f7-4087-bdd7-4c66efa020a9'}, {'quantity': 0.5, 'unit': {'id': '1047fd1c-d994-481f-a210-c81fefb30953', 'name': 'teaspoon', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': 'tsp', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.036386Z', 'updateAt': '2024-08-13T02:54:16.036390Z'}, 'food': {'id': '9a45b507-3b08-4c20-a76c-07e65d8afe7f', 'name': 'black pepper', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:58:18.030182Z', 'updateAt': '2024-10-12T22:58:18.030186Z'}, 'note': None, 'isFood': False, 'disableAmount': False, 'display': '¹/₂', 'title': None, 'originalText': '1/2 tsp Black Pepper', 'referenceId': '6cfa2c81-e609-47a5-a130-45f35ca962bc'}, {'quantity': 2.0, 'unit': {'id': 'a1b1c031-8017-4248-af3c-b80475675846', 'name': 'tablespoon', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'fraction': True, 'abbreviation': 'tbsp', 'pluralAbbreviation': '', 'useAbbreviation': False, 'aliases': [], 'createdAt': '2024-08-13T02:54:16.068349Z', 'updateAt': '2024-08-13T02:54:16.068352Z'}, 'food': {'id': '23a53064-4d7b-41af-a77d-e4a9a8a4673d', 'name': 'fish sauce', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:58:20.485591Z', 'updateAt': '2024-10-12T22:58:20.485594Z'}, 'note': '30g, optional', 'isFood': False, 'disableAmount': False, 'display': '2 30g, optional', 'title': None, 'originalText': '2 Tbsp (30g) Fish Sauce*, optional', 'referenceId': 'bccfc988-81e6-4f0e-b360-1ca89df04cb5'}, {'quantity': 3.0, 'unit': None, 'food': {'id': None, 'name': 'Bay Leaves', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': []}, 'note': 'optional', 'isFood': False, 'disableAmount': False, 'display': '3 optional', 'title': None, 'originalText': '3 Bay Leaves, optional', 'referenceId': 'dddf7d50-7446-4a76-afb9-4b42ad83fe04'}, {'quantity': 9.0, 'unit': None, 'food': {'id': '46974ebc-e8c4-4ccb-b9cc-dd468ceffe06', 'name': 'buitoni fresh fettuccine', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T23:09:23.086554Z', 'updateAt': '2024-10-12T23:09:23.086559Z'}, 'note': 'or your choice of pasta', 'isFood': False, 'disableAmount': False, 'display': '9 or your choice of pasta', 'title': None, 'originalText': '9 oz Buitoni Fresh Fettuccine , or your choice of pasta', 'referenceId': '29f94b54-15b8-4041-9658-520b619064dc'}, {'quantity': 0.5, 'unit': None, 'food': {'id': 'a5b53f98-34c8-4021-a9c3-deb8a2513099', 'name': 'pasta water', 'pluralName': None, 'description': '', 'extras': {}, 'onHand': False, 'labelId': None, 'aliases': [], 'label': None, 'createdAt': '2024-10-12T22:59:18.044221Z', 'updateAt': '2024-10-12T22:59:18.044227Z'}, 'note': None, 'isFood': False, 'disableAmount': False, 'display': '¹/₂', 'title': None, 'originalText': '1/2 C Pasta Water', 'referenceId': '369f8818-0c39-4f13-8521-f1a7d6a6076d'}]}

Please provide relevant logs

2024-10-20 09:53:16 File "/app/mealie/routes/recipe/recipe_crud_routes.py", line 382, in update_one 2024-10-20 09:53:16 recipe = self.service.update_one(slug, data) 2024-10-20 09:53:16 File "/app/mealie/services/recipe/recipe_service.py", line 343, in update_one 2024-10-20 09:53:16 new_data = self.repos.recipes.update(slug, update_data) 2024-10-20 09:53:16 File "/app/mealie/repos/repository_generic.py", line 191, in update 2024-10-20 09:53:16 entry.update(session=self.session, new_data) 2024-10-20 09:53:16 File "/app/mealie/db/models/_model_base.py", line 26, in update 2024-10-20 09:53:16 self.init(*args, *kwargs) 2024-10-20 09:53:16 File "", line 6, in init 2024-10-20 09:53:16 File "/app/mealie/db/models/recipe/api_extras.py", line 19, in wrapper 2024-10-20 09:53:16 return func(args, extras=extras, kwargs) 2024-10-20 09:53:16 File "/app/mealie/db/models/_model_utils/auto_init.py", line 145, in wrapper 2024-10-20 09:53:16 setattr(self, key, val) 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/attributes.py", line 537, in set 2024-10-20 09:53:16 self.impl.set( 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/attributes.py", line 1277, in set 2024-10-20 09:53:16 value = self.fire_replace_event( 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/attributes.py", line 1292, in fire_replace_event 2024-10-20 09:53:16 value = fn( 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/events.py", line 2567, in wrap 2024-10-20 09:53:16 fn(target, *arg) 2024-10-20 09:53:16 File "/app/mealie/db/models/recipe/recipe.py", line 250, in receive_name 2024-10-20 09:53:16 target.name_normalized = RecipeModel.normalize(value) 2024-10-20 09:53:16 File "/app/mealie/db/models/_model_base.py", line 17, in normalize 2024-10-20 09:53:16 return unidecode(val).lower().strip() 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/text_unidecode/init.py", line 10, in unidecode 2024-10-20 09:53:16 for ch in txt: 2024-10-20 09:53:16 TypeError: 'NoneType' object is not iterable 2024-10-20 09:53:16 INFO 2024-10-20T05:53:16 - [172.18.0.1:50978] 500 Internal Server Error "PUT /api/recipes/fettuccine-bolognese HTTP/1.1" 2024-10-20 09:53:16 ERROR 2024-10-20T05:53:16 - 'NoneType' object is not iterable 2024-10-20 09:53:16 Traceback (most recent call last): 2024-10-20 09:53:16 File "/app/mealie/routes/recipe/recipe_crud_routes.py", line 382, in update_one 2024-10-20 09:53:16 recipe = self.service.update_one(slug, data) 2024-10-20 09:53:16 File "/app/mealie/services/recipe/recipe_service.py", line 343, in update_one 2024-10-20 09:53:16 new_data = self.repos.recipes.update(slug, update_data) 2024-10-20 09:53:16 File "/app/mealie/repos/repository_generic.py", line 191, in update 2024-10-20 09:53:16 entry.update(session=self.session, new_data) 2024-10-20 09:53:16 File "/app/mealie/db/models/_model_base.py", line 26, in update 2024-10-20 09:53:16 self.init(*args, *kwargs) 2024-10-20 09:53:16 File "", line 6, in init 2024-10-20 09:53:16 File "/app/mealie/db/models/recipe/api_extras.py", line 19, in wrapper 2024-10-20 09:53:16 return func(args, extras=extras, kwargs) 2024-10-20 09:53:16 File "/app/mealie/db/models/_model_utils/auto_init.py", line 145, in wrapper 2024-10-20 09:53:16 setattr(self, key, val) 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/attributes.py", line 537, in set 2024-10-20 09:53:16 self.impl.set( 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/attributes.py", line 1277, in set 2024-10-20 09:53:16 value = self.fire_replace_event( 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/attributes.py", line 1292, in fire_replace_event 2024-10-20 09:53:16 value = fn( 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/sqlalchemy/orm/events.py", line 2567, in wrap 2024-10-20 09:53:16 fn(target, *arg) 2024-10-20 09:53:16 File "/app/mealie/db/models/recipe/recipe.py", line 250, in receive_name 2024-10-20 09:53:16 target.name_normalized = RecipeModel.normalize(value) 2024-10-20 09:53:16 File "/app/mealie/db/models/_model_base.py", line 17, in normalize 2024-10-20 09:53:16 return unidecode(val).lower().strip() 2024-10-20 09:53:16 File "/opt/pysetup/.venv/lib/python3.10/site-packages/text_unidecode/init.py", line 10, in unidecode 2024-10-20 09:53:16 for ch in txt: 2024-10-20 09:53:16 TypeError: 'NoneType' object is not iterable

Mealie Version

current

Deployment

Docker (Windows)

Additional Deployment Details

No response

michael-genson commented 3 days ago

Ah yup, it's because you're only passing recipeIngredient, but you have to pass everything else when using PUT. If you use PATCH it should work.

Marking this as a bug since you should receive a real error, not a 500 error