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
- Move BCC recipients out of `headers` and into the `bcc=` constructor argument: EmailMessage(bcc=['hidden@x.com']).
- Filter user-supplied header dictionaries to drop any 'bcc' key before passing to headers.
- 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
- Never put Bcc in headers; always use the bcc= constructor argument.
- Filter user-supplied header dicts to drop blocked names (bcc at minimum).
- Document for users that Bcc is transport-only and must not appear in the message.
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
- fail_silently cannot be used with a connection. Pass fail_si
- Invalid address {address!r}: {'; '.join(defects)}
- Invalid address {address!r}: must be a single address
- Invalid address "%s"
- Invalid address "{address}"
AI-assisted analysis of django/django@ae25a40be0 (2026-08-06).
Data as JSON: /api/errors/1014acd865c557d2.
Report an issue: GitHub.