Skip to content

fix(smtp): prevent double STARTTLS causing "Connection already using TLS" error#20

Merged
cofin merged 1 commit into
litestar-org:mainfrom
kyle-li-dev:fix/smtp-starttls-double-call
May 3, 2026
Merged

fix(smtp): prevent double STARTTLS causing "Connection already using TLS" error#20
cofin merged 1 commit into
litestar-org:mainfrom
kyle-li-dev:fix/smtp-starttls-double-call

Conversation

@kyle-li-dev

Copy link
Copy Markdown
Contributor

Summary

Fix SMTP STARTTLS double execution that causes EmailConnectionError: SMTP connection error: Connection already using TLS when using SMTPConfig(use_tls=True, use_ssl=False) with any STARTTLS-capable server (port 587).

Closes #19

Problem

When connecting to an SMTP server that supports STARTTLS (e.g., smtp.office365.com:587), the TLS upgrade is executed twice:

Step Actor What happens
1 litestar-email Creates aiosmtplib.SMTP without passing start_tls (defaults to None)
2 aiosmtplib start_tls=None → auto-detect → server supports STARTTLS → auto-upgrades
3 litestar-email use_tls=Truemanually calls starttls() again → 💥 Connection already using TLS

This affects all SMTP servers requiring STARTTLS (Office 365, Gmail, AWS SES, etc.), making the documented use_tls=True + use_ssl=False configuration completely broken.

Solution

Explicitly pass start_tls=False to aiosmtplib.SMTP() to disable its auto-STARTTLS negotiation, letting litestar-email manage the STARTTLS upgrade manually as intended:

self._connection = aiosmtplib.SMTP(
    hostname=self._config.host,
    port=self._config.port,
    timeout=self._config.timeout,
    use_tls=self._config.use_ssl,
    start_tls=False,  # Disable auto-STARTTLS; we manage it manually below
)

This is the minimal, non-breaking fix (Option A from the issue). The existing manual starttls() call remains the single point of TLS upgrade.

Changes

  • src/litestar_email/backends/smtp.py — Add start_tls=False parameter to aiosmtplib.SMTP() constructor
  • src/tests/test_smtp_backend.py — Add 3 regression tests:
    • test_smtp_backend_starttls_no_double_call — Verifies start_tls=False is passed and manual starttls() is called exactly once
    • test_smtp_backend_ssl_does_not_call_starttls — Verifies implicit SSL (use_ssl=True) does not trigger manual STARTTLS
    • test_smtp_backend_no_tls_no_ssl — Verifies plain connection does not call STARTTLS

Checklist

  • make test passes (129 tests)
  • make lint passes (ruff, mypy, pyright, slotscheck)
  • Regression tests added
  • No breaking changes

…mtplib

When use_tls=True and use_ssl=False, aiosmtplib's auto-STARTTLS
(start_tls=None default) would upgrade the connection, then
litestar-email's manual starttls() call would fail with
'Connection already using TLS'.

Pass start_tls=False to disable aiosmtplib's auto-negotiation,
letting litestar-email manage STARTTLS explicitly.

Add regression tests for STARTTLS, implicit SSL, and plain scenarios.
@kyle-li-dev

Copy link
Copy Markdown
Contributor Author

@cofin PTAL, thanks!

@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

@cofin cofin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kyle-li-dev

@cofin
cofin merged commit e57a4f9 into litestar-org:main May 3, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] SMTP STARTTLS executed twice causes Connection already using TLS error

3 participants