500 Internal Server Error in Django
Belirtiler
- Browser shows a plain white page with "500 Internal Server Error" (production) or Django's yellow debug page (development)
- Django error log contains a full Python traceback ending with an unhandled exception
- Sentry receives an "Unhandled Exception" event pointing to a specific line in views.py or models.py
- The error occurs on a specific URL or after a specific user action while other pages work fine
- Gunicorn log shows `[CRITICAL] WORKER TIMEOUT` if the exception hangs a request for too long
- Django error log contains a full Python traceback ending with an unhandled exception
- Sentry receives an "Unhandled Exception" event pointing to a specific line in views.py or models.py
- The error occurs on a specific URL or after a specific user action while other pages work fine
- Gunicorn log shows `[CRITICAL] WORKER TIMEOUT` if the exception hangs a request for too long
Kök Nedenler
- Unhandled Python exception in a view, model method, or middleware — any uncaught exception becomes a 500
- Missing or misconfigured environment variable — `settings.SECRET_KEY`, `DATABASE_URL`, or a required API key is empty, raising an error at first use
- Failed database migration — model references a column that doesn't exist yet in the schema
- Import error in any installed app or middleware — a typo in `INSTALLED_APPS` or a missing dependency crashes the whole app on startup
- Template rendering error — a variable access on `None` (e.g. `{{ user.profile.bio }}` when `profile` doesn't exist) with `TEMPLATE_STRING_IF_INVALID` not set
Tanı
**Step 1: Read the traceback from logs**
```bash
# Gunicorn stderr (most common)
sudo journalctl -u gunicorn --since '5 min ago' --no-pager
# Or tail a log file
tail -n 100 /var/www/myapp/logs/error.log
```
**Step 2: Enable DEBUG temporarily on a staging server**
```python
# Never enable DEBUG=True in production with real users
# On a staging clone:
DJANGO_ENV=development poe dev
# Then reproduce the exact URL — Django shows the full traceback
```
**Step 3: Run the request manually in the Django shell**
```bash
.venv/bin/python manage.py shell
```
```python
from django.test import RequestFactory
from myapp.views import my_view
rf = RequestFactory()
request = rf.get('/failing-path/')
my_view(request) # watch for the exception
```
**Step 4: Check for missing migrations**
```bash
.venv/bin/python manage.py showmigrations
.venv/bin/python manage.py migrate --check
```
**Step 5: Validate environment variables**
```bash
.venv/bin/python manage.py check --deploy
```
```bash
# Gunicorn stderr (most common)
sudo journalctl -u gunicorn --since '5 min ago' --no-pager
# Or tail a log file
tail -n 100 /var/www/myapp/logs/error.log
```
**Step 2: Enable DEBUG temporarily on a staging server**
```python
# Never enable DEBUG=True in production with real users
# On a staging clone:
DJANGO_ENV=development poe dev
# Then reproduce the exact URL — Django shows the full traceback
```
**Step 3: Run the request manually in the Django shell**
```bash
.venv/bin/python manage.py shell
```
```python
from django.test import RequestFactory
from myapp.views import my_view
rf = RequestFactory()
request = rf.get('/failing-path/')
my_view(request) # watch for the exception
```
**Step 4: Check for missing migrations**
```bash
.venv/bin/python manage.py showmigrations
.venv/bin/python manage.py migrate --check
```
**Step 5: Validate environment variables**
```bash
.venv/bin/python manage.py check --deploy
```
Çözüm
**Fix 1: Catch and handle the exception**
```python
# views.py
import logging
logger = logging.getLogger(__name__)
def my_view(request):
try:
result = do_something_risky()
except SomeSpecificError as e:
logger.exception('Expected error in my_view')
return HttpResponseServerError('Something went wrong')
return JsonResponse({'result': result})
```
**Fix 2: Apply pending migrations**
```bash
.venv/bin/python manage.py migrate
sudo systemctl restart gunicorn
```
**Fix 3: Fix a missing environment variable**
```bash
# .env.prod
REQUIRED_API_KEY=your_real_value_here
# Reload
sudo systemctl restart gunicorn
```
**Fix 4: Add a custom 500 error template**
```html
{# templates/500.html #}
<!DOCTYPE html>
<html>
<body>
<h1>Something went wrong</h1>
<p>We've been notified and are working on it.</p>
</body>
</html>
```
```python
# views.py
import logging
logger = logging.getLogger(__name__)
def my_view(request):
try:
result = do_something_risky()
except SomeSpecificError as e:
logger.exception('Expected error in my_view')
return HttpResponseServerError('Something went wrong')
return JsonResponse({'result': result})
```
**Fix 2: Apply pending migrations**
```bash
.venv/bin/python manage.py migrate
sudo systemctl restart gunicorn
```
**Fix 3: Fix a missing environment variable**
```bash
# .env.prod
REQUIRED_API_KEY=your_real_value_here
# Reload
sudo systemctl restart gunicorn
```
**Fix 4: Add a custom 500 error template**
```html
{# templates/500.html #}
<!DOCTYPE html>
<html>
<body>
<h1>Something went wrong</h1>
<p>We've been notified and are working on it.</p>
</body>
</html>
```
Önleme
- **Sentry integration**: Add `sentry-sdk[django]` so every unhandled exception is captured with a full traceback before users even notice
- **Strict null checks**: Use `get_object_or_404()` instead of `.get()` to return a clean 404 rather than a `DoesNotExist` → 500
- **CI migration check**: Run `manage.py migrate --check` in CI to catch missing migrations before deploy
- **Type annotations + mypy**: Catch `AttributeError` and `TypeError` at static analysis time rather than at runtime in production
- **Test the unhappy path**: Write unit tests for edge cases — empty querysets, None values, malformed inputs
- **Strict null checks**: Use `get_object_or_404()` instead of `.get()` to return a clean 404 rather than a `DoesNotExist` → 500
- **CI migration check**: Run `manage.py migrate --check` in CI to catch missing migrations before deploy
- **Type annotations + mypy**: Catch `AttributeError` and `TypeError` at static analysis time rather than at runtime in production
- **Test the unhappy path**: Write unit tests for edge cases — empty querysets, None values, malformed inputs