Web

Debug Django 500 Error on /menu/ - Decimal & Template Fixes

Fix intermittent Django 500s on /menu/ by debugging template rendering and DecimalField NaNs. Steps: locate NaNs, sanitize DB, add template guards and logging.

1 answer 1 view

Django 6.0 + Python 3.12: intermittent 500 Internal Server Errors on production when rendering /menu/ page — how to debug and fix?

I built a small site with Django 6.0 and Python 3.12. The /menu/ page pulls data from the database and intermittently returns 500 Internal Server Error in production; refreshing usually shows the page correctly. I cannot reproduce the error in development. The logs contain this traceback (relevant tail):

ERROR 2026-01-13 11:34:37,581 log 791 124526765520576 Internal Server Error: /menu/
Traceback (most recent call last):
File “/var/www/juliasempanadassite/venv/lib/python3.12/site-packages/django/core/handlers/exception.py”, line 55, in inner
response = get_response(request)
… (template rendering frames) …
File “/var/www/juliasempanadassite/venv/lib/python3.12/site-packages/django/utils/numberformat.py”, line 28, in format
if number is None or number == “”:
^^^^^^^^^^^^
File “”, line 119, in instancecheck

My menu view:

python
from django.shortcuts import render

from .models import Category, MenuItem, Highlight, Offer


def index(request):
 highlights = Highlight.objects.order_by("sort_order")
 offers = Offer.objects.order_by("sort_order")
 context = {"highlights": highlights, "offers": offers}
 return render(request, "menu/index.html", context)


def about(request):
 context = {}
 return render(request, "menu/about.html", context)


def menu(request):
 categories = Category.objects.order_by("sort_order")
 menu_items = MenuItem.objects.order_by("category_id", "sort_order")
 context = {"categories": categories, "menu_items": menu_items}

 return render(request, "menu/menu.html", context)

Key parts of the template (menu/menu.html):

django
{% for item in menu_items %}
<div class="col-sm-6 col-lg-4 all {{ item.category.short_name }}">
 ...
 <h6>
 {% if item.category.price_for_all == True %}
 ${{ item.category.price }}
 {% else %}
 ${{ item.price }}
 {% endif %}
 </h6>
</div>
{% endfor %}

Models:

python
from django.db import models


class Category(models.Model):
 name = models.CharField(max_length=200)
 short_name = models.CharField(max_length=20, null=True)
 price = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
 price_for_all = models.BooleanField(default=False)
 sort_order = models.IntegerField(default=1)

 def __str__(self):
 return self.name


class MenuItem(models.Model):
 category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
 name = models.CharField(max_length=200)
 description = models.TextField(blank=True, null=True)
 image = models.CharField(max_length=255, blank=True, null=True)
 price = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
 sort_order = models.IntegerField(default=1)

 def __str__(self):
 return self.name

This error appears only for the menu view and only in production. How can I diagnose and fix these random 500 Internal Server Errors that occur during template rendering? What should I check (for example: guard against None categories or prices in the template, Decimal/locale formatting issues, differences between dev and prod locale/format settings, database rows with unexpected values, or concurrency/WSGI worker issues), and what defensive code or template patterns should I apply to prevent these intermittent errors?

Most likely this intermittent Django 500 Error on /menu/ comes from an unexpected value (NaN, a non‑numeric string, or a missing Category) in a DecimalField that makes Django’s number-formatting code blow up only in production. Diagnose by forcing evaluation and inspecting the exact price/category values (or use the SQL trick to find NaNs), then fix by sanitizing the DB, adding model validation, and making the template/view defensive (safe filter, existence checks); add logging or Sentry so you get the full traceback next time.


Contents


Django 500 Error: read the traceback and what it means

Your traceback points at django/utils/numberformat.py while trying to format a number. That tells us the error happens during template rendering when Django tries to treat a value as a number. The extra clue — the frozen abc instancecheck frame — commonly appears when a value’s type or equality/comparison operation misbehaves (for example: Decimal(‘NaN’), a string saved into a numeric column, or a proxy object whose comparisons raise).

What’s worth checking first:

  • Does any MenuItem or Category row contain NaN or a non-numeric value in the DecimalField columns? NaN in a numeric column can break formatting later (see related Django ticket discussing NaN in DecimalField). See the Django numberformat implementation for where formatting logic runs: https://github.com/django/django/blob/main/django/utils/numberformat.py and the ticket that mentions NaN issues: https://code.djangoproject.com/ticket/33033.
  • Is item.category ever None (MenuItem.category is null=True and on_delete=SET_NULL)? If item.category is None and your template tries to access category attributes without checking, behavior can be unpredictable in templates.
  • Why only in production? Likely your production DB contains a bad record or your production locale/WSGI environment exercises code paths you don’t hit locally.

How to reproduce the failure locally (quick checks):

  • Run the same queries in a shell and inspect reprs:
python
from menu.models import MenuItem, Category
for m in MenuItem.objects.select_related('category').all():
 print(m.pk, repr(m.price), "cat:", None if m.category is None else (m.category.pk, repr(m.category.price)))
  • Test formatting explicitly (this is a light repro):
python
from django.utils import numberformat
for p in (m.price for m in MenuItem.objects.all()):
 print(repr(p), numberformat.format(p, decimal_pos=2))

If that second call raises for a particular value, you’ve found the offender.


Quick fixes for Django 500 Error in production

Ship these fast so users stop seeing 500s while you fix root causes.

  1. Fail gracefully in the template (short-term)
  • Replace direct dot-chains with presence checks. Example:
django
{% for item in menu_items %}
 {% with cat=item.category %}
 <h6>
 {% if cat and cat.price_for_all %}
 {% if cat.price %}${{ cat.price }}{% else %}{% endif %}
 {% elif item.price %}
 ${{ item.price }}
 {% else %}{% endif %}
 </h6>
 {% endwith %}
{% endfor %}
  • That prevents attribute errors when category is None, and reduces the chance an empty value gets passed into number-formatting.
  1. Eagerly evaluate and log before rendering
python
import logging
from decimal import Decimal, InvalidOperation
logger = logging.getLogger(__name__)

def menu(request):
 menu_items = list(MenuItem.objects.select_related('category').order_by('category_id','sort_order'))
 bad = []
 for item in menu_items:
 for val, name in ((item.price, 'item.price'),
 (item.category.price if item.category else None, 'category.price')):
 if val is None:
 continue
 try:
 Decimal(val)
 except (InvalidOperation, TypeError):
 bad.append({'item_id': item.pk, 'field': name, 'value': repr(val)})
 if bad:
 logger.error("Bad decimal values detected: %s", bad)
 return render(request, "menu/menu.html", {"menu_items": menu_items})
  • This surfaces bad values in logs so you can find the problematic rows.
  1. Add a temporary catch-all around render to avoid a full 500 while you investigate:
python
try:
 return render(request, "menu/menu.html", context)
except Exception:
 logger.exception("Error rendering /menu/")
 # Return a safe fallback page or 503
 return render(request, "menu/unavailable.html", status=503)

Django template defensive patterns (safe rendering)

Templates should never assume every DB value is perfectly sane. Two approaches work well:

  1. Template checks (no new code):
  • Test parent objects exist before accessing children: {% if item.category and item.category.price_for_all %}
  • Use |default_if_none for None: {{ item.price|default_if_none:"" }}
  1. Safer formatting via a custom template filter (recommended)
  • Create a small templatetag that validates and formats decimals:
python
# menu/templatetags/price_filters.py
from decimal import Decimal, InvalidOperation
from django import template

register = template.Library()

@register.filter
def display_price(value):
 try:
 if value is None:
 return ""
 d = Decimal(value)
 if d.is_nan() or not d.is_finite():
 return ""
 return f"{d:.2f}"
 except (InvalidOperation, TypeError):
 return ""
  • Use {{ cat.price|display_price }}. This avoids sending NaN or garbage into Django’s internal numberformatter and keeps the template safe.
  1. Move formatting into the model/view
  • Add a property on MenuItem/Category that returns a safe string; templates then just render {{ item.safe_price }}.

Database & model checks: find and fix NaN / bad decimals

Find bad rows quickly (PostgreSQL trick):

  • NaN is weird: it is not equal to itself. Use:
sql
SELECT id, price FROM menu_menuitem WHERE price <> price;
SELECT id, price FROM menu_category WHERE price <> price;

Those queries find rows where price is NaN. Back up before mutating data. To clear NaNs:

sql
UPDATE menu_menuitem SET price = NULL WHERE price <> price;
UPDATE menu_category SET price = NULL WHERE price <> price;

Django shell approach (safer if you prefer Python):

python
from decimal import Decimal
from menu.models import MenuItem, Category
bad = []
for m in MenuItem.objects.all():
 p = m.price
 try:
 if p is not None and (p.is_nan() or not p.is_finite()):
 bad.append(('item', m.pk, p))
 except Exception:
 bad.append(('item-ex', m.pk, repr(p)))
# then log/fix bad rows

Prevent future bad rows:

  • Add model validation (clean):
python
from django.core.exceptions import ValidationError
from decimal import InvalidOperation

def clean(self):
 super().clean()
 if self.price is not None:
 if getattr(self.price, "is_nan", lambda: False)() or not self.price.is_finite():
 raise ValidationError({"price": "Invalid price"})
  • Optionally call full_clean() in save() or ensure forms/serializers call full_clean.
  • Add unit tests that assert no NaNs/invalid decimals are allowed.

Locale, numberformat and Python 3.12 differences to check

Production and development locales and environment variables differ. Number formatting goes through Django’s formatting helpers; locale and settings like USE_L10N / USE_THOUSAND_SEPARATOR or environment LANG/LC_NUMERIC can affect behavior. If you see formatting oddities only in prod, compare:

  • Django settings in dev vs prod (LANGUAGE_CODE, USE_L10N)
  • OS environment (LANG, LC_ALL)
  • Python version and WSGI handler (some libraries behave slightly differently under Python 3.12)

For deeper reading on the formatting path inspect Django’s numberformat code: https://github.com/django/django/blob/main/django/utils/numberformat.py and the PRs/issues that changed formatting behavior: https://github.com/django/django/pull/18830.


Concurrency, WSGI workers and intermittent behavior

Intermittent errors often mean “only some requests hit bad data or some worker sees bad state.” Things to check:

  • Does your WSGI server (gunicorn/uwsgi/OpenLiteSpeed) pre-load or reuse global state? The OpenLiteSpeed forum notes Python 3.12 + WSGI behavior can differ: https://forum.openlitespeed.org/threads/python-3-12-wsgi-lsapi-and-django.6181/
  • Are writes happening concurrently that could temporarily leave bad values in the DB?
  • Do you mutate global decimal context (decimal.getcontext()) anywhere? That would be dangerous under concurrent workers.

Mitigations:

  • Use atomic writes for updates.
  • Avoid global mutable state in app module scope.
  • Force QuerySet evaluation (list()) and select_related so template errors surface in the view and can be logged.

Logging, monitoring and long-term safeguards

You need full production tracebacks and contextual data:

  • Enable Django error emails/ADMINS in production (when DEBUG=False) as described in Django docs: https://docs.djangoproject.com/en/3.2/howto/error-reporting/
  • Add structured logging around render and add the item id / repr(value) to logs so you can identify problematic rows. Use Sentry (or similar) to capture tracebacks and request context.
  • Add a periodic DB check (cron or management command) that searches for NaNs (the SQL above) and alerts if any are found.
  • Add model validators and tests so bad data is rejected before it reaches production.

Sources


Conclusion

This intermittent Django 500 Error on /menu/ is usually a data/formatting problem surfaced by template rendering (NaN or malformed DecimalField values, or missing category objects), and it shows up only in production because that environment contains the offending records or a different locale/WSGI setup. Short term: make the template and view defensive and add logging so you stop serving 500s and capture the bad value. Medium term: sanitize the DB (find and clear NaNs), add model validation (reject NaN), add unit tests, and enable production error reporting so future issues are visible immediately.

Authors
Verified by moderation
Moderation
Debug Django 500 Error on /menu/ - Decimal & Template Fixes