{"record":{"id":"c23af288557f525a","repo":"odysseus-dev/odysseus","slug":"email-account-cfg-get-account-name-or-account-c23af2","errorCode":null,"errorMessage":"Email account {cfg.get('account_name') or account_id} has no SMTP configured","messagePattern":"Email account (.+?) has no SMTP configured","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"routes/email_routes.py","lineNumber":1304,"sourceCode":"def _smtp_ready(cfg: dict) -> bool:\n    if not cfg.get(\"smtp_host\") or not cfg.get(\"smtp_user\"):\n        return False\n    return bool(cfg.get(\"smtp_password\") or cfg.get(\"oauth_provider\"))\n\n\ndef _resolve_send_config(account_id: str | None = None, owner: str = \"\") -> dict:\n    \"\"\"Resolve an account for outbound SMTP.\n\n    If the caller explicitly picked an account, use only that account and\n    return a clear error when it cannot send. If no account was picked and\n    the default is receive-only, fall back to the first SMTP-capable account\n    owned by the same user.\n    \"\"\"\n    cfg = _get_email_config(account_id, owner=owner)\n    if _smtp_ready(cfg):\n        return cfg\n    if account_id:\n        raise ValueError(f\"Email account {cfg.get('account_name') or account_id} has no SMTP configured\")\n    try:\n        from core.database import SessionLocal as _SL, EmailAccount as _EA\n        from sqlalchemy import and_, or_\n        db = _SL()\n        try:\n            q = db.query(_EA).filter(_EA.enabled == True)  # noqa: E712\n            if owner:\n                unowned = or_(_EA.owner == None, _EA.owner == \"\")  # noqa: E711\n                same_mailbox = or_(_EA.imap_user == owner, _EA.from_address == owner)\n                q = q.filter(or_(_EA.owner == owner, and_(unowned, same_mailbox)))\n            for row in q.order_by(_EA.is_default.desc(), _EA.created_at.asc()).all():\n                trial = _get_email_config(account_id=row.id, owner=owner)\n                if _smtp_ready(trial):\n                    return trial\n        finally:\n            db.close()\n    except Exception as e:\n        logger.debug(f\"SMTP-capable account fallback failed: {e}\")","sourceCodeStart":1286,"sourceCodeEnd":1322,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/email_routes.py#L1286-L1322","documentation":"ValueError from _resolve_send_config when the caller explicitly passed account_id but that account's config fails _smtp_ready (no usable smtp_host/credentials). Explicit account choice is honored strictly — no fallback — so a receive-only account selected for sending fails immediately with the account's name in the message.","triggerScenarios":"POST a send/schedule/test-email endpoint with account_id of an IMAP-only (receive-only) account, or a Google OAuth account whose SMTP side is not ready (see 465-style token failure during readiness check).","commonSituations":"UI defaulting to the first account which is the receive-only inbox; OAuth account whose token refresh fails at _smtp_ready time; account saved with smtp_host empty.","solutions":["Pick an account that has SMTP configured (smtp_host set, credentials or valid OAuth token).","Complete SMTP settings on the chosen account, or connect it via Google OAuth with gmail.send.","Alternatively omit account_id so the fallback can select the first SMTP-capable account owned by the caller."],"exampleFix":"# before\nsend_email(to, subject, body, account_id='inbox-only-acct')  # ValueError\n# after\nsend_email(to, subject, body)  # or account_id of an SMTP-capable account","handlingStrategy":"validation","validationCode":"const sendable = (await api.get('/api/emails/accounts')).filter(a => a.enabled && a.smtp_host);\nif (account_id && !sendable.some(a => a.id === account_id)) throw new Error('chosen account cannot send');","typeGuard":"function canSend(a: { enabled: boolean; smtp_host?: string | null }): boolean {\n  return Boolean(a.enabled && a.smtp_host);\n}","tryCatchPattern":"try { await sendWith(account_id); } catch (e) { if (/has no SMTP configured/.test(e.message)) { pickSmtpAccount(); } else throw e; }","preventionTips":["Restrict the send-account picker to SMTP-capable accounts.","Omit account_id to let the server fall back to the first capable account.","Catch ValueError and surface the account name in the UI message."],"tags":["smtp","email","configuration","send-only"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}