postgres ERROR: duplicate key value violates unique constraint during backfill re-run
Fixes postgres ERROR: duplicate key value violates unique constraint during a backfill re-run. Use it when a data backfill that partially completed is re-run and re-inserts rows it already wrote. Key trigger: duplicate-key errors naming the backfill's own rows on the second run.
postgres ERROR: duplicate key value violates unique constraint during backfill re-run
TL;DR: The backfill re-ran over rows it already wrote - it is not idempotent. Add ON CONFLICT DO NOTHING (or DO UPDATE) to the backfill INSERT so re-runs skip existing rows, and track completed batches in a checkpoint table so a re-run skips finished work entirely.
postgres ERROR: duplicate key value violates unique constraint during backfill re-runSteps
Identify the conflicting rows: the error names the constraint and the key value. Check whether those rows came from the first backfill run. Expected: confirmed - the re-run is re-inserting its own earlier output, not bad source data.
Make the write idempotent:
INSERT INTO target (id, data) SELECT id, data FROM source_batch ON CONFLICT (id) DO NOTHING;Use
DO UPDATEinstead ofDO NOTHINGif a re-run should refresh changed values. Expected: re-running the same batch inserts zero new rows and raises no error.Add a checkpoint: record completed batch ids in a small table and skip batches already marked done at the start of each run. Expected: a second full run finishes quickly with every batch skipped.
Re-run the backfill. Expected: completes with no duplicate-key errors; the target holds each source row exactly once.
Use this when
- Duplicate-key errors appear on the second (or later) run of a backfill or data migration.
- The first run partially completed before stopping.
- The conflicting rows match what the earlier run wrote.
Not for this skill when
- Duplicate-key errors appear on the FIRST run - the source data itself has duplicates; dedupe the source.
- Application writes race the backfill and collide - that is a locking/coordination problem, not backfill idempotency.
- You do not recognize the constraint name - find which table and key it guards before changing the write.
Variant phrasings
- duplicate key violates unique constraint backfill rerun
- postgres on conflict do nothing backfill idempotent
- data migration rerun duplicate rows
Why it happens
Without a conflict policy or a checkpoint, every re-run replays the same INSERT statements from the beginning. The unique constraint correctly refuses the second copy of each row - the error is the backfill's lack of idempotency, not corrupt data.
Edge cases
DO NOTHINGsilently skips rows that changed upstream since the first run - useDO UPDATEwhen freshness matters.- The checkpoint table needs its own transaction discipline: a checkpoint row that never commits recreates the double-run bug you just fixed.
- For very large backfills, keep each
ON CONFLICTstatement to one batch so a single statement never grows unbounded.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_DZHWzJ1XVZoOyyTFqpsXbw