# Deprecating or changing how django.core.mail handles fail\_silently

**URL:** <https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278>\
**Category:** Django Internals\
**Created:** [February 24, 2026, 12:33am UTC](https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278 "2026-02-24T00:33:22Z")\
**Posts on this page:** 1\
**Showing post:** 9

<div class="post-metadata">

**Author:** ![medmunds](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/medmunds/32/22140_2.png) [@medmunds](https://forum.djangoproject.com/u/medmunds)\
**Post date:** [March 11, 2026, 2:56am UTC](https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278/9 "2026-03-11T02:56:48Z")

</div>

Thanks everyone, the discussion has really helped.

New proposal:

- Django shouldn’t have a default email provider
- We need a way to send a message when email is configured but avoid an error if it’s not
- `fail_silently` is unsalvageable. Deprecate and remove it!

* * *

**No default email configuration**

Django’s current defaults—sending email through an SMTP server on localhost without auth— ~~are not useful in modern infrastructure~~ don’t work with many common server configurations. Your web server\[1\] ~~probably~~ often isn’t running SMTP. Or if it ~~somehow~~ _is,_ outbound mail ~~almost certainly~~ often requires auth. [Edited in response to Ken’s insights [below](https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278/11).]

In a fresh Django project, the current defaults mean trying to send email leads to a cryptic error like “ConnectionRefusedError: [Errno 61] Connection refused.” This comes up often enough that it’s covered in the [django-allauth FAQ](https://django-allauth.readthedocs.io/en/latest/faq.html#when-i-sign-up-i-run-into-connectivity-errors-connection-refused-et-al).

Unlike caches, databases, storages and tasks, there isn’t really a usable email provider to be the default. (We could try something like the console or dummy backends, but I suspect that would be at least as confusing as the current errors.)

I’m going to update the dep to make the default `EMAIL_PROVIDERS` empty: there is _no_ email provider defined by default. That means trying to send email in a fresh project will raise “InvalidEmailProvider: The email provider ‘default’ doesn’t exist.” (Hopefully no FAQ entry needed​🤞)

(I’m tempted to also add an empty EMAIL\_PROVIDERS dict to the settings template, with a comment that enabling email requires a “default” provider.)

* * *

**“Send email if configured” sending option**

Reusable apps need a clean way to say, “[I’d like to send some mail, so long as my users have email configured at all.](https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278/5)” Django needs it too:\[2\]

```python
class AdminEmailHandler(logging.Handler):
    def send_mail(self, subject, message, *args, **kwargs):
        if mail.providers.is_configured():
            mail.mail_admins(subject, message, *args, **kwargs)

```

The new `providers.is_configured(using="default")` checks if the `using` alias is defined in EMAIL\_PROVIDERS. This approach _doesn’t_ suppress any configuration or sending errors. If you’ve defined a default email provider but have a typo in the host, you want to know about that. Same thing if mail intended for admins is getting dropped because your mail server is unreachable.\[3\]

[Edit: The original version of this post had a different proposal—meant to accomplish the same thing—here. Check the diffs if you’re interested. Apologies for any confusion.]

* * *

**Deprecate `fail_silently` in sending APIs**

The semantics of `fail_silently=True` are unclear and backend dependent. Callers’ intent in using it varies widely—and rarely aligns with its actual behavior. It’s “[a language feature (try/except) as an argument](https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278/4)” with “[worse readability and hidden behavior](https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278/7).”

I’ve seen the light:\[4\] it’s time to deprecate the `fail_silently` arg to _all_ sending methods—send\_mail(), send\_mass\_mail(), EmailMessage.send(), mail\_admins() and mail\_managers().

One nuance: I _might_ keep the current `fail_silently` implementation _inside the smtp EmailBackend,_ but as configuration only. Someone who needs the “ignore smtp and some OS error” semantics could define an EMAIL\_PROVIDERS alias with `"fail_silently": True` in the OPTIONS. (So long as we deprecate and remove the send-time `fail_silently` arg, it doesn’t really matter if it’s still a backend option. The dep problem is the mismatch between the two.)

* * *

Thoughts?

The first two items would be part of the EMAIL\_PROVIDERS dep. We could still deprecate `fail_silently` ahead of that work.

* * *

1. or development machine 

2. I now believe Django’s AdminEmailHandler and BrokenLinkEmailsMiddleware are using `fail_silently=True` to suppress the cryptic errors caused by Django’s default unconfigured email settings (particularly with AdminEmailHandler, which is enabled by default in production). And _not_ because they want to hide configuration or network errors. But I could be reading it wrong. 

3. As a reminder, errors raised from the AdminEmailHandler do _not_ end up back in the AdminEmailHandler—there’s no worry about infinite loops. But they _will_ show up in your server error logs somewhere. 

4. The AIs had convinced me removing it entirely would be too disruptive. The humans have convinced me it’s the only pragmatic solution. (Thanks, humans!)

---

_[View the full topic](https://forum.djangoproject.com/t/deprecating-or-changing-how-django-core-mail-handles-fail-silently/44278)._
