51

I'm using Django, and every once in a while I get this error:

IntegrityError: duplicate key value violates unique constraint "myapp_mymodel_pkey"
DETAIL: Key (id)=(1) already exists.

My Postgres database does in fact have a myapp_mymodel object with the primary key of 1.

Why would Postgres attempt to use that primary key again? Or, is this most likely my application (or Django's ORM) causing this?

This issue occurred 3 more times in a row just now. What I've found is that when it does occur it happens one or more times in a row for a given table, then not again. It seems to happen for every table before it completely stops for days, happening for at least a minute or so per table when it does occur, and only happening intermittently (not all tables right away).

The fact that this error is so intermittent (happened only 3 or so times in 2 weeks - no other load on the DB, just me testing out my application) is what makes me so wary of a low-level problem.

1
  • Django specifically states that the primary key is generated by the DBMS unless specified - now, I don't know what @orokusaky was doing in his python code, but I ended up on this page because I'm quite confident that I have no code trying to use a specific primary key and I've never seen a DBMS trying to use a wrong one.
    – mccc
    Oct 9, 2015 at 8:32

7 Answers 7

66

PostgreSQL will not try to insert duplicate values on its own, it is you (your application, ORM included) who does.

It can be either a sequence feeding the values to the PK set to the wrong position and the table already containing the value equal to its nextval() - or simply that your application does the wrong thing. The first one is easy to fix:

SELECT setval('your_sequence_name', (SELECT max(id) FROM your_table));

The second one means debugging.

Django (or any other popular framework) doesn't reset sequences on its own - otherwise we would have similar questions every other day.

2
  • Is it worth noting (also based on @andi's answer herein) about the different isolation levels? For instance, if the second query comes in before the first is completed, is it possible to, given a scenario where I'm not using transactions, insert a record that results in getting max(id) before the first query completes, and then resulting in both having the same result?
    – orokusaki
    Nov 15, 2016 at 16:54
  • 1
    If you have a lot of fixtures using pks/ids, you might also be interested in fixing this in bulk: stackoverflow.com/questions/62059947/… Mar 28, 2022 at 8:50
17

You are most likely tying to insert a row in a table for which the serial column sequence value is not updated.

Consider following column in your table which is primary key defined by Django ORM for postgres

id serial NOT NULL

Whose default value is set to

nextval('table_name_id_seq'::regclass)

The sequence is only evaluated when the id field is set as blank. But that is problem if there are already entries into the table.

Question is why didn't those earlier entries trigger sequence update? That is because the id value were explicitly provided for all the earlier entries.

In my case those initial entries were loaded from fixtures through migrations.

This issue can also get tricky via custom entries with random PK value.

Say for eg. There are 10 entries into your table. You make an explicit entry with PK=15. The next four inserts through code would work perfectly fine but the 5th one would raise an exception.

DETAIL: Key (id)=(15) already exists.
2
  • Thank you for this post. I've been debugging a case like this for a long time. Very rarely did it occur. It turned out that a specific "manual" admin function could insert ids on its own, leaving the identity counter with an old value. This is a real peril with "GENERATED BY DEFAULT AS IDENTITY". I will think twice before using "BY DEFAULT" instead of "ALWAYS" the next time i define an identity column.
    – Michael
    Nov 21, 2019 at 15:00
  • This post was very helpful! I had the same issue with setting id's in fixtures. SQLite worked as expected, but PostgreSQL did not like it. Apr 30, 2021 at 11:37
4

I ended up here with very same error, which was occurring rarely, and was hard to track, because I was looking for it not where I should.

Fault was JS repetition which was doing the POST to the server twice! So sometimes it is worth to have a look not only on your django (or any other web framework) views and forms but also what happens on very front side.

2

Yeah, weird thing. In my case, something apparently went wrong during loading data in migrations. I added empty migration and wrote the lines to add some initial data, 6 records in my case.

db_alias = schema_editor.connection.alias
bulk = []
for item in items:
    bulk.append(MyModel(
        id=item[0],
        value=item[1],
        slug=item[2],
        name=item[3],
    ))

MyModel.objects.using(db_alias).bulk_create(bulk)

Then in admin panel I tried to add new item and got:

First attempt:

DETAIL:  Key (id)=(1) already exists.

Later attempts:

DETAIL:  Key (id)=(2) already exists.
DETAIL:  Key (id)=(3) already exists.
DETAIL:  Key (id)=(4) already exists.
DETAIL:  Key (id)=(5) already exists.
DETAIL:  Key (id)=(6) already exists.

And finally 7th and on times are all successful

So I am saying there may be something related to bulk_create as I loaded 6 items there. It maybe is something similar in your Django project causing that.

Django 1.9 PostgreSQL 9.3.14

1
  • I got the same error when I used the normal .create() method of django models. I had manually imported data into the table in my postgre Database which was why it resulted in this error. Dec 29, 2020 at 18:34
2

You can also change Sequence without any SQL script by GUI pgAdmin as below:

select your DB -> Schemas -> public -> Sequences -> right click -> properties -> Definition -> Current value.

1
1

This happened to me because I inserted records using literal, numeric values (instead of DEFAULT or undefined) as arguments for the auto-incremented column. Doing so circumvents the column's underlying sequence object's increment call, hence making the sequence's value out of sync with the values in the column in the table.

0

You can also do this:

ALTER SEQUENCE 'sequence_name_goes_here' name" RESTART WITH (SELECT max(id) + 1 FROM 'table_name_goes_here');

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.