{"record":{"id":"47cbbf24bb0a6d62","repo":"odysseus-dev/odysseus","slug":"imap-is-not-configured-for-account-cfg-get-accou","errorCode":null,"errorMessage":"IMAP is not configured for account {cfg.get('account_name') or 'default'!r}","messagePattern":"IMAP is not configured for account (.+?)","errorType":"exception","errorClass":"EmailNotConfiguredError","httpStatus":null,"severity":"warning","filePath":"routes/email_helpers.py","lineNumber":1214,"sourceCode":"    # \"got more than 1000000 bytes\" on UID SEARCH ALL.  (#2883)\n    imaplib._MAXLINE = 50_000_000\n    return conn\n\ndef _imap_connect(account_id: str | None = None, owner: str = \"\",\n                  timeout: int = _IMAP_TIMEOUT_SECONDS):\n    # SECURITY: passing `owner` scopes the fallback config lookup so a brand\n    # new user doesn't get connected against another user's default mailbox\n    # when they have no account configured.\n    #\n    # `timeout` is overridable so short-lived callers (e.g. the service-health\n    # probe) can impose a tighter budget than the default IMAP timeout.\n    cfg = _get_email_config(account_id, owner=owner)\n    # Send-only (SMTP-only) account: no IMAP host means there is no inbox to\n    # read. Bail out with a clear, typed error instead of handing an empty\n    # host to imaplib — IMAP4(\"\", 993) silently dials localhost:993 and fails\n    # with a confusing \"[Errno 111] Connection refused\" on every inbox poll.\n    if not cfg.get(\"imap_host\"):\n        raise EmailNotConfiguredError(\n            f\"IMAP is not configured for account {cfg.get('account_name') or 'default'!r}\"\n        )\n    # Connection mode:\n    #   STARTTLS on → plain + upgrade\n    #   STARTTLS off + port 993 → implicit SSL (IMAPS)\n    #   STARTTLS off + any other port → plain (local Dovecot, custom ports)\n    # The last branch is critical: previously this fell into IMAP4_SSL\n    # for any non-STARTTLS port, which would fail the TLS handshake on\n    # plain local servers (Dovecot on 31143, etc.).\n    conn = _open_imap_connection(\n        cfg[\"imap_host\"],\n        cfg[\"imap_port\"],\n        starttls=bool(cfg.get(\"imap_starttls\")),\n        timeout=timeout,\n    )\n    try:\n        if cfg.get(\"oauth_provider\") == \"google\":\n            token = _get_valid_google_token(cfg.get(\"account_id\"), cfg)","sourceCodeStart":1196,"sourceCodeEnd":1232,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/email_helpers.py#L1196-L1232","documentation":"EmailNotConfiguredError raised by the IMAP connection helper when the resolved account config has no imap_host. This is the typed guard for SMTP-only (send-only) accounts: without it imaplib would dial localhost:993 and fail with a confusing '[Errno 111] Connection refused'.","triggerScenarios":"Opening an inbox/folder/poll on an account configured with only smtp_host (e.g. a transactional send account), passing account_id of such an account to any read endpoint, or a default account that was never given IMAP settings.","commonSituations":"User sets up Gmail SMTP-only for sending then clicks Inbox; scheduled poller iterating all enabled accounts including send-only ones; default account row with empty IMAP fields.","solutions":["Fill in IMAP host/port for the account (e.g. imap.gmail.com:993) if it should receive mail.","If send-only is intended, stop calling inbox endpoints with that account_id and filter send-only accounts out of poll loops.","Catch EmailNotConfiguredError client-side and show a 'receive not configured' state instead of retrying."],"exampleFix":"# before\nfor acct in all_enabled_accounts:\n    with _imap(acct.id) as c: ...  # raises for send-only\n# after\nfor acct in all_enabled_accounts:\n    if not account_cfg(acct.id).get('imap_host'):\n        continue\n    with _imap(acct.id) as c: ...","handlingStrategy":"type-guard","validationCode":"const cfg = await api.getAccount(account_id);\nif (!cfg.imap_host) { markSendOnly(account_id); skipInboxPoll(account_id); }","typeGuard":"function canReceive(cfg: { imap_host?: string | null }): boolean { return Boolean(cfg.imap_host); }","tryCatchPattern":"try { with _imap(account_id, owner=owner) as c: ... }\nexcept EmailNotConfiguredError as e:\n    log.info(f'skip inbox for send-only account: {e}')","preventionTips":["Filter send-only accounts out of pollers and inbox UI.","Catch EmailNotConfiguredError distinctly from connection errors — it is config, not network.","Complete IMAP fields before advertising an inbox for the account."],"tags":["imap","email","configuration","send-only"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}