UnicodeError when sending password reset email

Hi, I use the authentication views from django.contrib.auth to my urls.py, and get an unexpected error:

UnicodeEncodeError at /reset_password/
'ascii' codec can't encode character '\u2019' in position 554: ordinal not in range(128)
[...]
23.        self.stream.write("%s\n" % msg_data)

This is surprising not just because my own code is not involved at all, but also because the message does not contain any obvious non-ASCII characters:

charset	
'utf-8'
message	
<django.core.mail.message.EmailMultiAlternatives object at 0x10aee1ad0>
msg	
<django.core.mail.message.SafeMIMEText object at 0x10b1a3010>
msg_data	
('Content-Type: text/plain; charset="utf-8"\n'
 'MIME-Version: 1.0\n'
 'Content-Transfer-Encoding: 8bit\n'
 'Subject: Password reset on lms.django\n'
 'From: webmaster@example.com\n'
 'To: testuser@example.com\n'
 'Date: Sat, 16 Mar 2024 09:44:44 -0000\n'
 'Message-ID: \n'
 ' <171058228430.54720.6766115132057211548@55.78.168.192.in-addr.arpa>\n'
 '\n'
 '\n'
 "You're receiving this email because you requested a password reset for your "
 'user account at lms.django.\n'
 '\n'
 'Please go to the following page and choose a new password:\n'
 '\n'
 'http://lms.django/reset/Mg/c3z72k-86698338ef6ab788941bf61dd916e\n'
 '\n'
 'Your username, in case you’ve forgotten: testuser\n'
 '\n'
 'Thanks for using our site!\n'
 '\n'
 'The lms.django team\n'
 '\n'
 '\n')
self	
<django.core.mail.backends.console.EmailBackend object at 0x10b249f10>

What could cause this error, why does Django not take its own DEFAULT_CHARSET (utf-8) into account when writing the message, and what can be done to solve the issue? Thanks in advance for any suggestions!

The unicode 2019 character is a “fancy single quote” character.

It looks like it’s because of this:

That makes sense, thank you! But why does Django compose a message it cannot handle? And how can I avoid/solve the issue?

That is interesting.

I’ve never encountered this issue myself, and we do use the “password reset” facility frequently.

(a) Hopefully someone more knowledgeable in this area is reading this thread and can provide an answer.

(b) I will look a little more deeply into exactly how we’re doing it to see if I can figure out what’s going on, but it may take me a bit to figure this out.

Are you using any non-default language-related settings?

There are some language settings in settings.py, but nothing else:

USE_I18N = True
WAGTAIL_I18N_ENABLED = True
USE_L10N = True

WAGTAIL_CONTENT_LANGUAGES = LANGUAGES = [
    ('de', "Deutsch"),
    ('en', "English"),
]
LANGUAGE_CODE = 'de'
LOCALE_PATHS = ['/var/www/locale']

For the password reset, there are just the views referenced in urls.py:

    path('reset_password/', auth_views.PasswordResetView.as_view(), name ='reset_password'),
    path('reset_password_sent/', auth_views.PasswordResetDoneView.as_view(), name ='password_reset_done'),
    path('reset/<uidb64>/<token>', auth_views.PasswordResetConfirmView.as_view(), name ='password_reset_confirm'),
    path('reset_password_complete/', auth_views.PasswordResetCompleteView.as_view(), name ='password_reset_complete'),

I found a solution! The problem was caused by uwsgi, and I had to add some environment variables to the configuration:

# uwsgi_lms.ini
env =LANG=en_US.utf8
env =LC_ALL=en_US.UTF-8
env =LC_LANG=en_US.UTF-8
env =PYTHONIOENCODING=UTF-8

Thanks for your help!

1 Like