Engineering7 min read

Fix Django IntegrityError: UNIQUE Constraint Failed — 5 Causes and Fixes

Django IntegrityError means a database constraint was violated. Here are the 5 most common causes — duplicate records, missing migrations, race conditions — and the exact fix for each.

DjangoPythonIntegrityErrorPostgreSQLdebugging

What Is Django IntegrityError?

Django raises IntegrityError when a database operation violates a constraint — UNIQUE, NOT NULL, FOREIGN KEY, or CHECK. Django doesn't catch these automatically. They bubble up as unhandled exceptions unless you wrap the database call.

Error: django.db.utils.IntegrityError: UNIQUE constraint failed: auth_user.username

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.

Cause 1: Duplicate Value on UNIQUE Field

Most common cause. You're inserting or updating a record with a value that already exists on a field marked unique=True.

python
# models.py
class UserProfile(models.Model):
    username = models.CharField(max_length=100, unique=True)
    email = models.EmailField()

# ❌ Two users with same username → IntegrityError
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.

# ✅ get_or_create — no duplicate, no error
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:

# In a data migration — deduplicate before adding constraint
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.

# ❌ Race condition — check and insert are not atomic
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 references customer_id=999 which doesn't exist
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.

Debug faster starting today.

Free VS Code extension. 10 sessions/day. No credit card.

Install Free →

Related Posts

Engineering

Codebase-Aware AI Debugging vs Generic AI: Why Context Changes Everything

7 min read

Engineering

How to Debug a React Application in VS Code (Complete Guide)

8 min read

← All posts