django/django · error · InvalidMailer

MAILERS[{alias!r}]: OPTIONS must define 'host'.

Error message

MAILERS[{alias!r}]: OPTIONS must define 'host'.

What it means

Raised by the MAILERS-based SMTP email backend (alias is not None) when host is None after construction. It is an InvalidMailer error, message prefixed with MAILERS[<alias>]:. Unlike the deprecated path which falls back to settings.EMAIL_HOST, the MAILERS path requires an explicit 'host' in OPTIONS because the new system intentionally has no implicit default host. The check is at smtp.py:85-86.

Source

Thrown at django/core/mail/backends/smtp.py:86

            )
            if self.use_ssl and self.use_tls:
                raise ValueError(
                    "EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so "
                    "only set one of those settings to True."
                )
            return

        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.use_tls = use_tls if use_tls is not None else False
        self.use_ssl = use_ssl if use_ssl is not None else False
        self.timeout = timeout
        self.ssl_keyfile = ssl_keyfile
        self.ssl_certfile = ssl_certfile
        if self.host is None:
            raise InvalidMailer("OPTIONS must define 'host'.", alias=self.alias)
        if self.use_ssl and self.use_tls:
            raise InvalidMailer(
                "The 'use_ssl' and 'use_tls' OPTIONS are incompatible. "
                "Set at most one of them to True.",
                alias=self.alias,
            )
        if self.port is None:
            if self.use_ssl:
                self.port = 465
            elif self.use_tls:
                self.port = 587
            else:
                self.port = 25

    @property
    def connection_class(self):
        return smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP

View on GitHub (pinned to ae25a40be0)

Solutions

  1. Add 'host' to the alias OPTIONS, e.g. OPTIONS: {'host': 'smtp.example.com', 'port': 587}.
  2. Validate at startup: `assert 'host' in MAILERS['transactional']['OPTIONS'], 'host required'`.
  3. Keep port and credentials consistent in the same OPTIONS dict.

Example fix

# before
MAILERS = {
    "default": {
        "BACKEND": "django.core.mail.backends.smtp.EmailBackend",
        "OPTIONS": {"port": 587, "username": "u", "password": "p"},
    },
}

# after
MAILERS = {
    "default": {
        "BACKEND": "django.core.mail.backends.smtp.EmailBackend",
        "OPTIONS": {
            "host": "smtp.example.com",
            "port": 587,
            "username": "u",
            "password": "p",
            "use_tls": True,
        },
    },
}
Defensive patterns

Strategy: validation

Validate before calling

from django.conf import settings

def validate_smtp_mailers_have_host():
    for alias, cfg in getattr(settings, "MAILERS", {}).items():
        if cfg.get("BACKEND") == "django.core.mail.backends.smtp.EmailBackend":
            if not cfg.get("OPTIONS", {}).get("host"):
                raise ImproperlyConfigured(
                    f"MAILERS[{alias!r}] SMTP backend requires OPTIONS['host']"
                )

Type guard

def is_valid_smtp_mailer(cfg: dict) -> bool:
    return (
        cfg.get("BACKEND") == "django.core.mail.backends.smtp.EmailBackend"
        and isinstance(cfg.get("OPTIONS"), dict)
        and isinstance(cfg["OPTIONS"].get("host"), str)
        and bool(cfg["OPTIONS"]["host"])
    )

Try / catch

from django.core.mail import InvalidMailer
from django.core import mail
try:
    mail.mailers["default"]
except InvalidMailer as e:
    if "OPTIONS must define 'host'" in str(e):
        raise SystemExit("Add OPTIONS['host'] to the SMTP MAILERS alias.")
    raise

Prevention

When it happens

Trigger: Defining a MAILERS alias with BACKEND 'django.core.mail.backends.smtp.EmailBackend' whose OPTIONS omits 'host', then accessing mailers[<alias>] or sending with using=<alias>.

Common situations: Migrating from EMAIL_HOST to MAILERS and forgetting to copy host into OPTIONS; aliasing a copy of the default config and stripping the host; typo 'Host' vs 'host' (the option is lowercase).

Related errors


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