Skip to content

Emails with attachments cause server error in Django 5.2 #182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kevinrenskers opened this issue Apr 14, 2025 · 5 comments
Closed

Emails with attachments cause server error in Django 5.2 #182

kevinrenskers opened this issue Apr 14, 2025 · 5 comments
Labels

Comments

@kevinrenskers
Copy link

kevinrenskers commented Apr 14, 2025

I am sending a EmailMultiAlternatives with an attachment, and this causes a server error in Django 5.2.

Code:

from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.db import models
from django.template.loader import render_to_string
from weasyprint import HTML


class Order(models.Model):
    # fields...

    def send_order_placed_email(self):
        text_body = render_to_string("shop/orders/order_placed_email.txt", {"order": self, "settings": settings})
        html_body = render_to_string("shop/orders/order_placed_email.html", {"order": self, "settings": settings})

        invoice_html = render_to_string("shop/orders/invoice.html", {"order": self, "settings": settings})
        invoice_pdf = HTML(string=invoice_html).write_pdf(cache=pdf_cache)

        message = EmailMultiAlternatives(
            subject="Thank you for your purchase!",
            body=text_body,
            to=[self.email],
        )
        message.attach_alternative(html_body, "text/html")
        message.attach("invoice.pdf", invoice_pdf, "application/pdf")
        message.send()

Error:

>>> order = Order.objects.get(id=42679955)
>>> order.send_order_placed_email()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/kevin/Workspace/api.example.com/shop/orders/models.py", line 247, in send_order_placed_email
    message.send()
    ~~~~~~~~~~~~^^
  File "/Users/kevin/Workspace/api.example.com/.venv/lib/python3.13/site-packages/django/core/mail/message.py", line 307, in send
    return self.get_connection(fail_silently).send_messages([self])
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
  File "/Users/kevin/Workspace/api.example.com/.venv/lib/python3.13/site-packages/mailer/backend.py", line 12, in send_messages
    messages = Message.objects.bulk_create([Message(email=email) for email in email_messages], MESSAGES_BATCH_SIZE)
                                            ~~~~~~~^^^^^^^^^^^^^
  File "/Users/kevin/Workspace/api.example.com/.venv/lib/python3.13/site-packages/django/db/models/base.py", line 558, in __init__
    _setattr(self, prop, value)
    ~~~~~~~~^^^^^^^^^^^^^^^^^^^
  File "/Users/kevin/Workspace/api.example.com/.venv/lib/python3.13/site-packages/mailer/models.py", line 148, in _set_email
    self.message_data = email_to_db(val)
                        ~~~~~~~~~~~^^^^^
  File "/Users/kevin/Workspace/api.example.com/.venv/lib/python3.13/site-packages/mailer/models.py", line 94, in email_to_db
    return base64_encode(pickle.dumps(email)).decode("ascii")
                         ~~~~~~~~~~~~^^^^^^^
_pickle.PicklingError: Can't pickle <class 'django.core.mail.message.Attachment'>: attribute lookup Attachment on django.core.mail.message failed
@almiavicas
Copy link

almiavicas commented Apr 14, 2025

Just found the same issue in Django 5.2, python 3.12. Here is my code:

from typing import List
import traceback
from django.core.validators import validate_email
from django.core.mail import EmailMultiAlternatives
from django.contrib import messages
from django.utils.translation import gettext as _
from django.template.loader import get_template

def send_email(subject, plain_template, html_template, context, to: List[str], attachments=None, request=None):
    try:
        plain_text = get_template(plain_template)
        html = get_template(html_template)
        text_content = plain_text.render(context)
        html_content = html.render(context)
        msg = EmailMultiAlternatives(subject, text_content, to=to, attachments=attachments)
        msg.mixed_subtype = 'related'
        msg.attach_alternative(html_content, "text/html")
        msg.send(fail_silently=True)
    except Exception as e:
        if request:
            messages.error(request, _('Ocurrió un error al enviar el correo electrónico.'))
        print(e)
        print(traceback.format_exc())
django-1          | Traceback (most recent call last):
django-1          |   File "/app/core/services.py", line 82, in send_email
django-1          |     msg.send(fail_silently=True)
django-1          |   File "/usr/local/lib/python3.12/site-packages/django/core/mail/message.py", line 307, in send
django-1          |     return self.get_connection(fail_silently).send_messages([self])
django-1          |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
django-1          |   File "/usr/local/lib/python3.12/site-packages/mailer/backend.py", line 12, in send_messages
django-1          |     messages = Message.objects.bulk_create([Message(email=email) for email in email_messages], MESSAGES_BATCH_SIZE)
django-1          |                                             ^^^^^^^^^^^^^^^^^^^^
django-1          |   File "/usr/local/lib/python3.12/site-packages/django/db/models/base.py", line 558, in __init__
django-1          |     _setattr(self, prop, value)
django-1          |   File "/usr/local/lib/python3.12/site-packages/mailer/models.py", line 148, in _set_email
django-1          |     self.message_data = email_to_db(val)
django-1          |                         ^^^^^^^^^^^^^^^^
django-1          |   File "/usr/local/lib/python3.12/site-packages/mailer/models.py", line 94, in email_to_db
django-1          |     return base64_encode(pickle.dumps(email)).decode("ascii")
django-1          |                          ^^^^^^^^^^^^^^^^^^^
django-1          | _pickle.PicklingError: Can't pickle <class 'django.core.mail.message.Alternative'>: attribute lookup Alternative on django.core.mail.message failed

@kevinrenskers
Copy link
Author

@kevinrenskers
Copy link
Author

Django 5.2.1 will fix this regression and make emails pickleable again.

@tim-hub
Copy link

tim-hub commented Apr 28, 2025

I think I got the same issue when sending html and text emails at the same time

@kevinrenskers
Copy link
Author

Since Django 5.2.1 has been released I think we can safely close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants