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.
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 “
My menu view:
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):
{% 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:
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
- Quick fixes for Django 500 Error in production
- Django template defensive patterns (safe rendering)
- Database & model checks: find and fix NaN / bad decimals
- Locale, numberformat and Python 3.12 differences to check
- Concurrency, WSGI workers and intermittent behavior
- Logging, monitoring and long-term safeguards
- Sources
- Conclusion
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:
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):
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.
- Fail gracefully in the template (short-term)
- Replace direct dot-chains with presence checks. Example:
{% 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.
- Eagerly evaluate and log before rendering
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.
- Add a temporary catch-all around render to avoid a full 500 while you investigate:
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:
- Template checks (no new code):
- Test parent objects exist before accessing children:
{% if item.category and item.category.price_for_all %} - Use
|default_if_nonefor None:{{ item.price|default_if_none:"" }}
- Safer formatting via a custom template filter (recommended)
- Create a small templatetag that validates and formats decimals:
# 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.
- 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:
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:
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):
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):
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
- https://www.reddit.com/r/django/comments/1888vns/server_error_500_in_production_of_a_django_app_on/
- https://stackoverflow.com/questions/66080047/django-framework-got-server-error-500-when-debug-to-false
- https://learndjango.com/tutorials/customizing-django-404-and-500-error-pages
- https://docs.djangoproject.com/en/3.2/howto/error-reporting/
- https://github.com/django/django/blob/main/django/utils/numberformat.py
- https://stackoverflow.com/questions/14120640/bad-operand-type-for-abs-str-in-django/14121133#14121133
- https://code.djangoproject.com/ticket/33033
- https://stackoverflow.com/questions/16982070/server-error-500-on-django-when-template-debug-is-set-to-false
- https://studygyaan.com/django/how-to-use-custom-500-error-template-in-django
- https://github.com/django/django/pull/18830
- https://stackoverflow.com/questions/29961332/how-to-debug-intermittent-errors-from-django-app-served-with-gunicorn-possible
- https://stackoverflow.com/questions/5343700/race-condition-in-django
- https://forum.openlitespeed.org/threads/python-3-12-wsgi-lsapi-and-django.6181/
- https://github.com/encode/django-rest-framework/issues/5181
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.