hermes - ✅(Solved) Fix [Bug]: Email gateway fails on SMTP port 465 — uses STARTTLS instead of SMTP_SSL [1 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#11842Fetched 2026-04-18 05:58:35
View on GitHub
Comments
1
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
closed ×1commented ×1labeled ×1

Error Message

  1. Observe logs: ERROR gateway.platforms.email: [Email] SMTP connection failed: Connection unexpectedly closed: timed out

Additional Logs / Traceback (optional)

Root Cause

Root Cause Analysis (optional)

PR fix notes

PR #12161: fix(email): add email SMTP STARTTLS fallback

Description (problem / solution / changelog)

What does this PR do?

Fixes SMTP connection handling in the email gateway so both implicit TLS on port 465 and STARTTLS on port 587 work correctly.

Previously, the adapter expects a plain SMTP connection upgraded with STARTTLS on port 587 and can not handle SMTP servers with port 465 with SMTP SSL. This PR unifies SMTP connection paths behind a single flow:

  1. Probe whether the target SMTP server supports STARTTLS
  2. If STARTTLS is supported, connect with smtplib.SMTP(...) and upgrade via starttls(...)
  3. Otherwise, connect with smtplib.SMTP_SSL(...)

This keeps 587-compatible servers working while preserving support for 465-only SMTP setups.

Related Issue

Fixes #11842

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Unified SMTP connection setup in gateway/platforms/email.py
  • Added STARTTLS capability probing before selecting the SMTP transport
  • Updated all 3 SMTP call sites to use the shared connection logic:
    • SMTP connection test in connect()
    • Message sending in _send_email()
    • Attachment sending in _send_email_with_attachment()
  • Added regression coverage in tests/gateway/test_email.py for:
    • STARTTLS-capable SMTP servers
    • SMTP_SSL fallback when STARTTLS is unavailable
    • Send, send-with-attachment, and connect flows

How to Test

  1. Configure the email gateway with an SMTP server that uses STARTTLS on port 587 and verify connect() and message sending still work
  2. Configure the email gateway with an SMTP server that uses implicit TLS on port 465 and verify connect() and message sending work via SMTP_SSL
  3. Run:
    • scripts/run_tests.sh tests/gateway/test_email.py -q
    • scripts/run_tests.sh tests/gateway/ -q

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Linux

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

N/A

Changed files

  • gateway/platforms/email.py (modified, +69/-18)
  • tests/gateway/test_email.py (modified, +86/-1)

Code Example

Report       https://paste.rs/ce00d
agent.log    https://paste.rs/AuSdu
gateway.log  https://paste.rs/mVTVz

---



---

smtp = smtplib.SMTP(self._smtp_host, self._smtp_port, timeout=30)                                                                                                                        
     smtp.starttls(context=ssl.create_default_context())

---

if self._smtp_port == 465:                                                                                                                                                               
         smtp = smtplib.SMTP_SSL(self._smtp_host, self._smtp_port, timeout=30,                                                                                                                
                                 context=ssl.create_default_context())                                                                                                                        
     else:                                                                                                                                                                                    
         smtp = smtplib.SMTP(self._smtp_host, self._smtp_port, timeout=30)                                                                                                                    
         smtp.starttls(context=ssl.create_default_context())
RAW_BUFFERClick to expand / collapse

Bug Description

The email platform adapter always uses smtplib.SMTP() with starttls() for SMTP connections, regardless of port. Port 465 requires implicit TLS (smtplib.SMTP_SSL), so connections to port 465 timeout during the STARTTLS handshake.

Affected file:
gateway/platforms/email.py — lines 294-296, 512-515, 604-607

Steps to Reproduce

 1. Set `EMAIL_SMTP_PORT=465` in `~/.hermes/.env` (standard SMTPS port used by Migadu, Fastmail, etc.)                                                                                    
 2. Start the gateway: `hermes gateway start`                                                                                                                                             
 3. Observe logs: `ERROR gateway.platforms.email: [Email] SMTP connection failed: Connection unexpectedly closed: timed out`                                                              
 4. The gateway enters `retrying` state and keeps failing every ~5 minutes

Expected Behavior

Connect to email.

Actual Behavior

Times out.

Affected Component

Setup / Installation, Other

Messaging Platform (if gateway-related)

No response

Debug Report

Report       https://paste.rs/ce00d
agent.log    https://paste.rs/AuSdu
gateway.log  https://paste.rs/mVTVz

Operating System

Ubuntu 24.04

Python Version

No response

Hermes Version

No response

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

```python                                                                                                                                                                                
 smtp = smtplib.SMTP(self._smtp_host, self._smtp_port, timeout=30)                                                                                                                        
 smtp.starttls(context=ssl.create_default_context())                                                                                                                                      
 ```                                                                                                                                                                                      
 This always does plain TCP + STARTTLS upgrade. On port 465 the server expects immediate TLS and never responds to the STARTTLS command, causing a timeout.

Proposed Fix (optional)

Use smtplib.SMTP_SSL when port is 465, smtplib.SMTP + starttls when port is 587:

 ```python                                                                                                                                                                                
 if self._smtp_port == 465:                                                                                                                                                               
     smtp = smtplib.SMTP_SSL(self._smtp_host, self._smtp_port, timeout=30,                                                                                                                
                             context=ssl.create_default_context())                                                                                                                        
 else:                                                                                                                                                                                    
     smtp = smtplib.SMTP(self._smtp_host, self._smtp_port, timeout=30)                                                                                                                    
     smtp.starttls(context=ssl.create_default_context())                                                                                                                                  
 ```

This pattern applies to all three SMTP connection sites: connect(), _send_email(), and _send_email_with_attachment().

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

extent analysis

TL;DR

Use smtplib.SMTP_SSL for SMTP connections on port 465 to establish an immediate TLS connection.

Guidance

  • Identify the SMTP port used in the email platform adapter and adjust the connection method accordingly.
  • For port 465, use smtplib.SMTP_SSL to establish an immediate TLS connection.
  • For other ports (e.g., 587), use smtplib.SMTP with starttls() to upgrade to a TLS connection.
  • Verify the fix by checking the logs for successful SMTP connections and email sending.

Example

if self._smtp_port == 465:
    smtp = smtplib.SMTP_SSL(self._smtp_host, self._smtp_port, timeout=30, context=ssl.create_default_context())
else:
    smtp = smtplib.SMTP(self._smtp_host, self._smtp_port, timeout=30)
    smtp.starttls(context=ssl.create_default_context())

Notes

This fix applies to all three SMTP connection sites: connect(), _send_email(), and _send_email_with_attachment() in the gateway/platforms/email.py file.

Recommendation

Apply the proposed workaround by modifying the SMTP connection code to use smtplib.SMTP_SSL for port 465 and smtplib.SMTP with starttls() for other ports, as this will establish the correct TLS connection for each port.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Bug]: Email gateway fails on SMTP port 465 — uses STARTTLS instead of SMTP_SSL [1 pull requests, 1 comments, 1 participants]