{"record":{"id":"0ab676daa4a5ce02","repo":"odysseus-dev/odysseus","slug":"no-smtp-capable-email-account-configured-0ab676","errorCode":null,"errorMessage":"No SMTP-capable email account configured","messagePattern":"No SMTP-capable email account configured","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"routes/email_routes.py","lineNumber":1323,"sourceCode":"    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}\")\n    raise ValueError(\"No SMTP-capable email account configured\")\n\n\ndef _store_email_flag(conn, uid: str, flag: str, add: bool = True) -> bool:\n    # imaplib's plain store() takes a message SEQUENCE NUMBER, not a UID, so the\n    # old `else` fallback flagged whichever message happened to occupy sequence\n    # position == the UID value. When the UID isn't present, fail safe (callers\n    # surface \"Email not found\") rather than touch an unrelated message.\n    if not _uid_exists(conn, uid):\n        return False\n    op = \"+FLAGS\" if add else \"-FLAGS\"\n    status, _ = conn.uid(\"STORE\", _uid_bytes(uid), op, flag)\n    return status == \"OK\"\n\n\ndef _move_email_message(conn, uid: str, dest: str, role: str = \"\") -> bool:\n    dest = _resolve_mail_folder(conn, dest, role or _folder_role_from_name(dest))\n    # copy()/store() are SEQUENCE-NUMBER commands; using them with a UID (the old\n    # `else` branch) copied + \\Deleted-flagged the wrong message and then","sourceCodeStart":1305,"sourceCodeEnd":1341,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/email_routes.py#L1305-L1341","documentation":"ValueError raised at the end of _resolve_send_config: no explicit account was given, the default account is not SMTP-ready, and the fallback scan over all enabled accounts owned by the caller (including legacy ownerless rows matching their mailbox) found none that passes _smtp_ready. The intermediate per-account errors are only logged at DEBUG.","triggerScenarios":"Calling send endpoints on a fresh install with zero accounts; all accounts receive-only; every SMTP-capable account disabled (enabled=False); Google accounts whose token refresh fails during the trial loop.","commonSituations":"First-run send attempt before any account setup; account created via auto-discover that populated IMAP but left SMTP blank; all accounts toggled off but UI still allows compose.","solutions":["Add and enable at least one account with SMTP host + credentials (or Google OAuth with send scope).","Check server DEBUG logs for 'SMTP-capable account fallback failed' to see why each candidate was rejected.","In the UI, block compose/send until _resolve_send_config could succeed (at least one enabled SMTP account)."],"exampleFix":"# before\nawait send_email(to=..., subject=..., body=...)  # no accounts -> ValueError\n# after\nif not any(a.enabled and a.smtp_host for a in accounts):\n    raise UserVisibleError('Configure an outgoing email account first')\nawait send_email(to=..., subject=..., body=...)","handlingStrategy":"validation","validationCode":"const capable = (await api.get('/api/emails/accounts')).filter(a => a.enabled && a.smtp_host);\nif (capable.length === 0) { promptAccountSetup(); return; }","typeGuard":"function hasSmtpAccount(accts: Array<{enabled: boolean; smtp_host?: string | null}>): boolean {\n  return accts.some(a => a.enabled && a.smtp_host);\n}","tryCatchPattern":"try { await sendEmail(...); } catch (e) { if (/No SMTP-capable email account/.test(e.message)) { promptAccountSetup(); return; } throw e; }","preventionTips":["Block compose/send in the UI until at least one enabled SMTP account exists.","Enable DEBUG logs to see why each fallback candidate was rejected.","After auto-discover, verify smtp_host was populated, not just imap_host."],"tags":["smtp","email","configuration","no-accounts"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}