Skip to content

Supabase database errorSupabase: “duplicate key value violates unique constraint”

New rows stop saving with duplicate key value violates unique constraint "orders_pkey", Postgres error 23505. The constraint name says which kind you have. A _pkey error on an id the database picks itself usually means the id counter fell behind, typically after you imported data. Any other name, such as profiles_email_key, means the app really is inserting a row that already exists. Here are both, in Supabase’s and Postgres’s words, or hand it over at a fixed price.

Short answer

If the constraint ends in _pkey and the id is generated, compare max(id) with the next sequence value and reset the sequence with setval. If it names another column, find out why the same row is inserted twice: a double submit, a webhook delivered again, or a trigger and the app both creating the same profile. Then use an upsert or on conflict do nothing.

Read the constraint name, then fix the cause

  1. 01

    A _pkey error after an import: the sequence fell behind

    “it likely means that the table's sequence has somehow become out of sync, likely because of a mass import process (or something along those lines).” Supabase, Inserting into Sequence/Serial Table Causes "duplicate key violates unique constraint" Error

    Rows imported or restored with their ids, or seed scripts that set ids by hand, don’t move the counter that hands out new ids. The next insert gets an id that is already taken. It only hits tables whose id is generated, and it keeps failing until the sequence is reset.

  2. 02

    Check the sequence, then reset it

    “This error suggests a discrepancy between the sequence-generated IDs and the actual IDs in the table.” Supabase, Why are there gaps in my Postgres id sequence?

    Run this in the SQL editor with your table and column. If the next value is not higher than the largest id, move the sequence up to the largest id, so the next new row gets a free number. Supabase suggests taking a backup first.

    select max(id) from public.orders;
    select nextval(pg_get_serial_sequence('public.orders', 'id'));
    
    select setval(
      pg_get_serial_sequence('public.orders', 'id'),
      (select max(id) from public.orders)
    );
  3. 03

    Any other constraint, or a _pkey on an id you set: the row really exists

    “ON CONFLICT can be used to specify an alternative action to raising a unique constraint or exclusion constraint violation error.” PostgreSQL, INSERT

    An error on users_email_key, or on profiles_pkey where the id comes from the signed-in user, means the insert repeats a value that must be unique. The usual sources are a button tapped twice, a Stripe webhook delivered more than once, or a profile row created by a trigger on sign-up and again by the app. Decide which write should win, and make the other one safe to repeat. In SQL:

    -- inside a sign-up trigger function: keep the first row
    insert into public.profiles (id, username)
    values (new.id, new.raw_user_meta_data ->> 'username')
    on conflict (id) do nothing;
  4. 04

    From the app: upsert instead of insert

    In supabase-js, upsert with onConflict updates the row when it already exists. Under RLS it needs both an insert policy and an update policy for the user, or it fails.

    await supabase.from('profiles').upsert(
      { id: user.id, username },
      { onConflict: 'id' }
    );
  5. 05

    Don’t paper over it

    “sequences guarantee uniqueness, but not consecutiveness” Supabase, Why are there gaps in my Postgres id sequence?

    Catching 23505 and ignoring it hides the double write, and an upsert that runs where the first write should have won can overwrite good data. Fix the duplicate at its source: disable the button while saving, record processed webhook event ids, or let only the trigger create profiles. Gaps in ids after a fix are normal.

Checked against the vendor’s docs on : Supabase troubleshooting: duplicate key violates unique constraint on a sequence table; Supabase troubleshooting: Why are there gaps in my Postgres id sequence?; PostgreSQL docs: INSERT … ON CONFLICT.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: Supabase “Database error saving new user” · Stripe webhook returns 200 but the database isn’t updated · Supabase “User already registered” · Supabase new row violates RLS

Questions

More on this error

Will resetting the sequence change my existing ids?

No. setval only changes the next number the sequence hands out. Existing rows keep their ids, and new rows continue from a number above the largest one.

Why does it fail on some inserts and not others?

With an out-of-sync sequence, each failed insert still moves the counter forward. Inserts fail until the counter passes ids that are taken, then start working again, which makes it look random. Resetting the sequence ends it.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.