odysseus-dev/odysseus · error · ValueError
Email account {cfg.get('account_name') or account or 'defaul
Error message
Email account {cfg.get('account_name') or account or 'default'} has no SMTP configured What it means
Raised by _smtp_connect when the cfg it received (loaded from the account argument or the default config) fails the same _smtp_ready() check. It is the low-level guard hit after _resolve_send_config already returned a cfg — typically when callers pass cfg directly.
Source
Thrown at mcp_servers/email_server.py:1334
def _resolve_send_config(account=None):
cfg = _load_config(account)
if _smtp_ready(cfg):
return account, cfg
if account:
raise ValueError(f"Email account {cfg.get('account_name') or account} has no SMTP configured")
for row in _list_accounts_raw():
selector = row.get("id") or row.get("name") or row.get("imap_user")
trial = _load_config(selector)
if _smtp_ready(trial):
return selector, trial
raise ValueError("No SMTP-capable email account configured")
def _smtp_connect(account=None, cfg=None):
"""Connect to SMTP server, returns logged-in connection."""
cfg = cfg or _load_config(account)
if not _smtp_ready(cfg):
raise ValueError(f"Email account {cfg.get('account_name') or account or 'default'} has no SMTP configured")
port = int(cfg.get("smtp_port") or 465)
security = str(cfg.get("smtp_security") or "").strip().lower()
if security not in {"ssl", "starttls", "none"}:
security = "starttls" if port == 587 else "ssl"
if security == "starttls":
conn = smtplib.SMTP(
cfg["smtp_host"],
port,
timeout=EMAIL_SOCKET_TIMEOUT,
)
try:
conn.starttls()
except Exception:
# Don't leak the open plain socket on a rejected STARTTLS. SMTP has
# no shutdown(); close() is the low-level socket close (no QUIT). (#3174)
try:
conn.close()
except Exception:View on GitHub (pinned to f9235ebbf1)
Solutions
- Ensure the cfg passed to _smtp_connect comes from _resolve_send_config (which validates and falls back), not a hand-built dict
- Fill in the missing SMTP fields on the account/config
- Check that SMTP password decryption succeeds — an empty decrypted value leaves smtp_password falsy
Example fix
# before conn = _smtp_connect(account, cfg=imap_only_cfg) # after account, cfg = _resolve_send_config(account) conn = _smtp_connect(account, cfg=cfg)
Defensive patterns
Strategy: type-guard
Validate before calling
if cfg is None or not _smtp_ready(cfg):
_, cfg = _resolve_send_config(account) Type guard
def smtp_cfg_ready(cfg: dict) -> bool:
return bool(cfg) and _smtp_ready(cfg) Try / catch
try:
_smtp_connect(account, cfg)
except ValueError as e:
if 'no SMTP configured' in str(e):
account, cfg = _resolve_send_config(account)
_smtp_connect(account, cfg)
raise Prevention
- Always obtain send cfg from _resolve_send_config, never hand-build it
- Assert _smtp_ready(cfg) before connecting
- Test with a smoke-send after config changes
When it happens
Trigger: _smtp_connect(account, cfg) invoked with a cfg dict missing smtp_host/smtp_user/smtp_password, or _smtp_connect(account) where that account has no SMTP fields.
Common situations: Code paths that build a partial cfg from IMAP-only data and forward it to the send path; account rows where the encrypted SMTP password failed to decrypt into smtp_password.
Related errors
- Email account {cfg.get('account_name') or account} has no SM
- No SMTP-capable email account configured
- Email account {cfg.get('account_name') or account_id} has no
- No SMTP-capable email account configured
- Unsubscribe failed
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/21c0266acc4d8ff1.
Report an issue: GitHub.