Skip to content

Fix ZeroDivisionError in unit conversion causing recipe retrieval failure #4072

@freakadings

Description

@freakadings

Tandoor Version

2.2.6

Setup

Docker / Docker-Compose

Reverse Proxy

Nginx Proxy Manager (NPM)

Other

psql16

Bug description

When clicking on certain recipes, the API returned an internal server error due to a division by zero in the unit conversion helper.

I traced the culprit down bychecking the "tandoor"-database via:

SELECT * FROM public.cookbook_unitconversion
WHERE converted_amount = 0 OR converted_amount IS NULL;

identified where "converted_amount" was "0"

entered the id at the end of the unit-convertion url:
http://tandoor.local/edit/UnitConversion/170
and entered a value above zero via the webui of tandoor.

Which fixed the error and i could browse through tandoor as usual.

I checked if I could enter it again and since i could, it seems there's no safeguard against converted_amount being zero (or null).

It's strange because the conversion was nothing i added recently. Probably the error appeared after an update?

Happy to provide more logs or database info as needed.

Relevant logs

MainThread ERROR 2025-09-25 21:39:43,450 django.request Internal Server Error: /api/recipe/57/
Traceback (most recent call last):
  File "/opt/recipes/venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/opt/recipes/venv/lib/python3.13/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/opt/recipes/venv/lib/python3.13/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
    return view_func(request, *args, **kwargs)
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/views.py", line 515, in dispatch
    response = self.handle_exception(exc)
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/views.py", line 475, in handle_exception
    self.raise_uncaught_exception(exc)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
    raise exc
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/views.py", line 512, in dispatch
    response = handler(request, *args, **kwargs)
  File "/opt/recipes/venv/lib/python3.13/site-packages/drf_spectacular/drainage.py", line 207, in wrapped_method
    return method(self, request, *args, **kwargs)
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/mixins.py", line 56, in retrieve
    return Response(serializer.data)
                    ^^^^^^^^^^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/serializers.py", line 573, in data
    ret = super().data
          ^^^^^^^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/serializers.py", line 251, in data
    self._data = self.to_representation(self.instance)
                 ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/serializers.py", line 540, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
                            ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/serializers.py", line 716, in to_representation
    self.child.to_representation(item) for item in iterable
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/serializers.py", line 540, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
                            ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/serializers.py", line 716, in to_representation
    self.child.to_representation(item) for item in iterable
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/serializers.py", line 540, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
                            ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File "/opt/recipes/venv/lib/python3.13/site-packages/rest_framework/fields.py", line 1870, in to_representation
    return method(value)
  File "/opt/recipes/cookbook/serializer.py", line 976, in get_conversions
    for c in uch.get_conversions(obj):
             ~~~~~~~~~~~~~~~~~~~^^^^^
  File "/opt/recipes/cookbook/helper/unit_conversion_helper.py", line 120, in get_conversions
    r = self._uc_convert(c, ingredient.amount, ingredient.unit, ingredient.food)
  File "/opt/recipes/cookbook/helper/unit_conversion_helper.py", line 142, in _uc_convert
    return Ingredient(amount=amount * (uc.base_amount / uc.converted_amount), unit=uc.base_unit, food=food, space=self.space)
                                       ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
decimal.DivisionByZero: [<class 'decimal.DivisionByZero'>]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions