django/django · error · ValueError

Bcc is not a valid email header. Use the "bcc" argument to s

Error message

Bcc is not a valid email header. Use the "bcc" argument to specify blind carbon copy recipients.

What it means

Inside EmailMessage.message() (django/core/mail/message.py:365), while copying user-supplied extra_headers onto the MIME message, a header whose lowercased name equals 'bcc' is rejected. BCC recipients must never appear as a message header — they are SMTP-envelope-only — and must be supplied through the dedicated `bcc` constructor argument so they are not written into the message text.

Source

Thrown at django/core/mail/message.py:365

        self._set_list_header_if_not_empty(msg, "Cc", self.cc)
        self._set_list_header_if_not_empty(msg, "Reply-To", self.reply_to)

        # Email header names are case-insensitive (RFC 2045), so we have to
        # accommodate that when doing comparisons.
        header_names = [key.lower() for key in self.extra_headers]
        if "date" not in header_names:
            if settings.EMAIL_USE_LOCALTIME:
                tz = get_current_timezone()
            else:
                tz = timezone.utc
            msg["Date"] = datetime.now(tz)
        if "message-id" not in header_names:
            # Use cached DNS_NAME for performance
            msg["Message-ID"] = make_msgid(domain=DNS_NAME)
        for name, value in self.extra_headers.items():
            header = name.lower()
            if header == "bcc":
                raise ValueError(
                    'Bcc is not a valid email header. Use the "bcc" '
                    "argument to specify blind carbon copy recipients."
                )
            # Avoid headers handled above.
            if header not in {"from", "to", "cc", "reply-to"}:
                msg[name] = force_str(value, strings_only=True)
        self._idna_encode_address_header_domains(msg)
        return msg

    def recipients(self):
        """
        Return a list of all recipients of the email (includes direct
        addressees as well as Cc and Bcc entries).
        """
        return [email for email in (self.to + self.cc + self.bcc) if email]

    def send(self, fail_silently=False, *, using=None):
        """Send the email message."""

View on GitHub (pinned to ae25a40be0)

Solutions

  1. Move BCC recipients out of `headers` and into the `bcc=` constructor argument: EmailMessage(bcc=['hidden@x.com']).
  2. Filter user-supplied header dictionaries to drop any 'bcc' key before passing to headers.
  3. If BCC must be hidden per-recipient, send one message per recipient instead.

Example fix

// before
EmailMessage(headers={"Bcc": "hidden@example.com"})
// after
EmailMessage(bcc=["hidden@example.com"])
Defensive patterns

Strategy: validation

Validate before calling

def sanitize_headers(headers):
    blocked = {"bcc"}
    return {k: v for k, v in (headers or {}).items() if k.lower() not in blocked}

Try / catch

try:
    msg = EmailMessage(headers=headers, bcc=bcc_list).message()
except ValueError as e:
    if "bcc" in str(e).lower():
        # move bcc out of headers and retry
        bcc_list = headers.pop("bcc")
        msg = EmailMessage(headers=headers, bcc=bcc_list).message()

Prevention

When it happens

Trigger: EmailMessage(headers={'Bcc': 'hidden@x.com'}) or headers={'bcc': 'hidden@x.com'} — any header whose lowercased name equals 'bcc' triggers the ValueError at line 365.

Common situations: Porting code that set Bcc as a raw header in older email libraries; configuration that lets users add arbitrary headers; misunderstanding that Bcc is transport-only and must not appear in the message.

Related errors


AI-assisted analysis of django/django@ae25a40be0 (2026-08-06). Data as JSON: /api/errors/1014acd865c557d2. Report an issue: GitHub.