The error always includes the table name and column name after the colon. That's your starting point — find which model maps to that table, find
which field has the constraint, and trace back to where you're writing that field.
Most common cause. You're inserting or updating a record with a value that already exists on a field marked unique=True.
class UserProfile(models.Model):
username = models.CharField(max_length=100, unique=True)
email = models.EmailField()
UserProfile.objects.create(username="alice", email="a@example.com")
UserProfile.objects.create(username="alice", email="b@example.com")
▎ Fix: Use get_or_create when you want "create if not exists", or update_or_create when you want upsert behavior.
profile, created = UserProfile.objects.get_or_create(
username="alice",
defaults={"email": "a@example.com"}
)
Cause 2: Missing or Unapplied Migration After Model Change
Adding unique=True to an existing field, or adding a new unique constraint, requires a migration. If you forget to run it, the DB schema and your
Django model disagree — inserts that Django thinks are valid fail at the DB level.
▎ Error: django.db.utils.IntegrityError: UNIQUE constraint failed: myapp_product.sku
▎ Fix: Check migration status first.
python manage.py showmigrations myapp
python manage.py makemigrations
python manage.py migrate
If the constraint was added to an existing field with existing data, you need to handle existing duplicates before migrating:
from django.db import migrations
def deduplicate_sku(apps, schema_editor):
Product = apps.get_model('myapp', 'Product')
seen = set()
for product in Product.objects.order_by('created_at'):
if product.sku in seen:
product.sku = f"{product.sku}-{product.id}"
product.save()
seen.add(product.sku)
class Migration(migrations.Migration):
dependencies = [('myapp', '0012_previous')]
operations = [migrations.RunPython(deduplicate_sku)]
Cause 3: Race Condition on Concurrent Inserts
Two requests arrive simultaneously, both check "does this username exist?" → both get False → both try to insert → one succeeds, one raises
IntegrityError. The UNIQUE constraint is doing its job — the bug is the check-then-insert pattern.
def register_user(username, email):
if not User.objects.filter(username=username).exists():
User.objects.create(username=username, email=email)
▎ Fix: Don't check first. Try the insert and catch IntegrityError.
from django.db import IntegrityError
# ✅ Atomic — no race condition
def register_user(username, email):
try:
user = User.objects.create(username=username, email=email)
return user, True # created
except IntegrityError:
user = User.objects.get(username=username)
return user, False # already existed
▎ Note: This pattern (try-insert, catch-and-get) is safe under concurrent load. get_or_create does the same thing internally using
▎ select_for_update or atomic transactions depending on database backend.
Cause 4: NOT NULL Constraint on a Field Without a Default
Adding a new required field to an existing model without providing a default fails when existing rows are migrated. Django prompts you during
makemigrations — but if you bypass the prompt or script migrations manually, the NOT NULL violation hits at migration time.
▎ Error: django.db.utils.IntegrityError: NOT NULL constraint failed: myapp_order.customer_id
▎ Fix: Always provide a default when adding a NOT NULL field to an existing table, or make it nullable during migration, then backfill, then
▎ remove the null.
# Migration approach: nullable first, then backfill, then not-null
class Migration(migrations.Migration):
operations = [
# Step 1: add nullable
migrations.AddField(
model_name='order',
name='customer_id',
field=models.IntegerField(null=True),
),
]
# In next migration: backfill + add constraint
Cause 5: FOREIGN KEY Violation
Inserting a row that references a non-existent parent record. Common when creating related objects out of order, or when the parent was deleted
and on_delete wasn't set correctly.
▎ Error: django.db.utils.IntegrityError: insert or update on table "myapp_order" violates foreign key constraint
Order.objects.create(customer_id=999, total=49.99)
▎ Fix: Ensure parent exists before creating child, or use get_object_or_404 at the view layer to validate the ID early.
from django.shortcuts import get_object_or_404
def create_order(request, customer_id):
customer = get_object_or_404(Customer, id=customer_id)
order = Order.objects.create(customer=customer, total=49.99)
return order
How to Catch IntegrityError Gracefully
In views and API handlers, wrap DB operations to return a useful error instead of a 500:
from django.db import IntegrityError
from django.http import JsonResponse
def create_user_view(request):
try:
user = User.objects.create(
username=request.POST['username'],
email=request.POST['email']
)
return JsonResponse({"id": user.id}, status=201)
except IntegrityError as e:
return JsonResponse({"error": "Username already taken"}, status=409)
Quick Reference
┌────────────────────────────┬─────────────────────────────────┬──────────────────────────────────────────┐
│ IntegrityError type │ Cause │ Fix │
├────────────────────────────┼─────────────────────────────────┼──────────────────────────────────────────┤
│ UNIQUE constraint failed │ Duplicate value on unique field │ get_or_create or catch + return existing │
├────────────────────────────┼─────────────────────────────────┼──────────────────────────────────────────┤
│ NOT NULL constraint failed │ Field missing value, no default │ Add default or use nullable migration │
├────────────────────────────┼─────────────────────────────────┼──────────────────────────────────────────┤
│ Foreign key violation │ Parent record doesn't exist │ Validate parent ID before insert │
├────────────────────────────┼─────────────────────────────────┼──────────────────────────────────────────┤
│ Check constraint violation │ Value outside allowed range │ Validate at model level before saving │
└────────────────────────────┴─────────────────────────────────┴──────────────────────────────────────────┘
For race condition bugs specifically — IntegrityErrors that appear under load but not locally — paste the view + model into DebugAI. It identifies
check-then-insert patterns and suggests the atomic equivalent.