I'm migrating from Ubuntu 20.04, mailman 3.2.2, postgresql 12 to Debian 13, mailman 3.3.10, postgresql 17.
After migrating the postgresql databases and assuring the perms and passwords work, this is what I get with a "mailman-web migrate":
(user@domain.com) redacted from the original
# mailman-web migrate
Operations to perform:
Apply all migrations: account, admin, auth, contenttypes, django_mailman3, django_q, hyperkitty, postorius,
sessions, sites, socialaccount
Running migrations:
Applying account.0003_alter_emailaddress_create_unique_verified_email... OK
Applying account.0004_alter_emailaddress_drop_unique_email... OK
Applying account.0005_emailaddress_idx_upper_email... OK
Applying account.0006_emailaddress_lower...Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/django/db/backends/utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "account_emailaddress_user_id_
email_987c8728_uniq"
DETAIL: Key (user_id, email)=(24, user@domain.com) already exists.
The above exception was the direct cause of the following exception:
The above exception was the direct cause of the following exception:
Traceback (most recent call last): File "/usr/share/mailman3-web/manage.py", line 10, in <module> execute_from_command_line(sys.argv) ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^ File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 442, in execute_from_command_line utility.execute() ~~~~~~~~~~~~~~~^^ File "/usr/lib/python3/dist-packages/django/core/management/__init__.py", line 436, in execute self.fetch_command(subcommand).run_from_argv(self.argv) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/django/core/management/base.py", line 412, in run_from_argv self.execute(*args, **cmd_options) ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/django/core/management/base.py", line 458, in execute output = self.handle(*args, **options) File "/usr/lib/python3/dist-packages/django/core/management/base.py", line 106, in wrapper res = handle_func(*args, **kwargs) File "/usr/lib/python3/dist-packages/django/core/management/commands/migrate.py", line 356, in handle post_migrate_state = executor.migrate( targets, ...<3 lines>... fake_initial=fake_initial, ) File "/usr/lib/python3/dist-packages/django/db/migrations/executor.py", line 135, in migrate state = self._migrate_all_forwards( state, plan, full_plan, fake=fake, fake_initial=fake_initial ) File "/usr/lib/python3/dist-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards state = self.apply_migration( state, migration, fake=fake, fake_initial=fake_initial ) File "/usr/lib/python3/dist-packages/django/db/migrations/executor.py", line 252, in apply_migration state = migration.apply(state, schema_editor) File "/usr/lib/python3/dist-packages/django/db/migrations/migration.py", line 132, in apply operation.database_forwards( ~~~~~~~~~~~~~~~~~~~~~~~~~~~^ self.app_label, schema_editor, old_state, project_state ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/usr/lib/python3/dist-packages/django/db/migrations/operations/special.py", line 193, in database_forwards self.code(from_state.apps, schema_editor) ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/allauth/account/migrations/0006_emailaddress_lower.py", line 11, in forwards EmailAddress.objects.all().exclude(email=Lower("email")).update( ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ email=Lower("email") ^^^^^^^^^^^^^^^^^^^^ ) ^ File "/usr/lib/python3/dist-packages/django/db/models/query.py", line 1206, in update rows = query.get_compiler(self.db).execute_sql(CURSOR) File "/usr/lib/python3/dist-packages/django/db/models/sql/compiler.py", line 1984, in execute_sql cursor = super().execute_sql(result_type) File "/usr/lib/python3/dist-packages/django/db/models/sql/compiler.py", line 1562, in execute_sql cursor.execute(sql, params) ~~~~~~~~~~~~~~^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/django/db/backends/utils.py", line 67, in execute return self._execute_with_wrappers( ~~~~~~~~~~~~~~~~~~~~~~~~~~~^ sql, params, many=False, executor=self._execute ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/usr/lib/python3/dist-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers return executor(sql, params, many, context) File "/usr/lib/python3/dist-packages/django/db/backends/utils.py", line 84, in _execute with self.db.wrap_database_errors: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3/dist-packages/django/db/utils.py", line 91, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/usr/lib/python3/dist-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) django.db.utils.IntegrityError: duplicate key value violates unique constraint "account_emailaddress_user_id_email_987c8728_uniq" DETAIL: Key (user_id, email)=(24, user@domain.com) already exists.
On 11/12/25 11:48, pashdown@xmission.com wrote:
I'm migrating from Ubuntu 20.04, mailman 3.2.2, postgresql 12 to Debian 13, mailman 3.3.10, postgresql 17.
After migrating the postgresql databases and assuring the perms and passwords work, this is what I get with a "mailman-web migrate":
(user@domain.com) redacted from the original
# mailman-web migrate Operations to perform: Apply all migrations: account, admin, auth, contenttypes, django_mailman3, django_q, hyperkitty, postorius,
sessions, sites, socialaccount Running migrations: Applying account.0003_alter_emailaddress_create_unique_verified_email... OK Applying account.0004_alter_emailaddress_drop_unique_email... OK Applying account.0005_emailaddress_idx_upper_email... OK Applying account.0006_emailaddress_lower...Traceback (most recent call last): File "/usr/lib/python3/dist-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^ psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "account_emailaddress_user_id_
email_987c8728_uniq" DETAIL: Key (user_id, email)=(24, user@domain.com) already exists.
This is actually a django-allauth issue. This issue is due to changes in Django allauth to prevent creating multiple addresses differing only in case. The problem is if you have such multiple addresses in your account_emailaddress table, this migration fails. There is a script at https://www.msapiro.net/scripts/UC_fix that will fix this. You may need to adjust the shebang and replace psycopg2 with sqlite3.
See https://github.com/pennersr/django-allauth/issues/3019 perhaps starting at This issue is due to changes in Django allauth to prevent creating multiple addresses differing only in case. The problem is if you have such multiple addresses in your account_emailaddress table, this migration fails. There is a script at https://www.msapiro.net/scripts/UC_fix that will fix this. You may need to adjust the shebang.
See https://github.com/pennersr/django-allauth/issues/3019 perhaps starting at https://github.com/pennersr/django-allauth/issues/3019#issuecomment-17480282...
-- Mark Sapiro <mark@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan
On 11/12/25 12:09, Mark Sapiro wrote:
This is actually a django-allauth issue. This issue is due to changes in Django allauth to prevent creating multiple addresses differing only in case. The problem is if you have such multiple addresses in your account_emailaddress table, this migration fails. There is a script at https://www.msapiro.net/scripts/UC_fix that will fix this. You may need to adjust the shebang and replace psycopg2 with sqlite3.
See https://github.com/pennersr/django-allauth/issues/3019 perhaps starting at This issue is due to changes in Django allauth to prevent creating multiple addresses differing only in case. The problem is if you have such multiple addresses in your account_emailaddress table, this migration fails. There is a script at https://www.msapiro.net/scripts/UC_fix that will fix this. You may need to adjust the shebang.
See https://github.com/pennersr/django-allauth/issues/3019 perhaps starting at https://github.com/pennersr/django-allauth/issues/3019#issuecomment-17480282...
That was a bit of a copy/paste messup. It should have said just
This is actually a django-allauth issue. This issue is due to changes in Django allauth to prevent creating multiple addresses differing only in case. The problem is if you have such multiple addresses in your account_emailaddress table, this migration fails. There is a script at https://www.msapiro.net/scripts/UC_fix that will fix this. You may need to adjust the shebang.
See https://github.com/pennersr/django-allauth/issues/3019 perhaps starting at https://github.com/pennersr/django-allauth/issues/3019#issuecomment-17480282...
-- Mark Sapiro <mark@msapiro.net> The highway is for gamblers, San Francisco Bay Area, California better use your sense - B. Dylan
Thank you Mark. I was able to fix the problem with this bit of Claude code for "mailman-web shell":
from allauth.account.models import EmailAddress from django.db.models.functions import Lower
# Get all email addresses all_emails = EmailAddress.objects.all()
# Group by user_id and lowercase email seen = {} to_delete = []
for email_obj in all_emails: key = (email_obj.user_id, email_obj.email.lower())
if key in seen:
# We have a duplicate - decide which to keep
existing = seen[key]
# Keep verified over unverified
if email_obj.verified and not existing.verified:
to_delete.append(existing.id)
seen[key] = email_obj
# Keep primary over non-primary
elif email_obj.primary and not existing.primary:
to_delete.append(existing.id)
seen[key] = email_obj
# Keep lowercase version
elif email_obj.email == email_obj.email.lower():
to_delete.append(existing.id)
seen[key] = email_obj
else:
to_delete.append(email_obj.id)
else:
seen[key] = email_obj
print(f"Will delete {len(to_delete)} duplicate entries")
# Delete duplicates if to_delete: EmailAddress.objects.filter(id__in=to_delete).delete() print("Duplicates removed!")
participants (2)
-
Mark Sapiro -
pashdown@xmission.com