odysseus-dev/odysseus · error · ValueError

No email account is configured for the authenticated owner

Error message

No email account is configured for the authenticated owner

What it means

Raised by the email MCP server's _load_config when an MCP owner is authenticated and the accounts database contains rows, but _filter_accounts_for_owner() leaves zero rows for that owner. It is a ValueError thrown before any IMAP connection is attempted, meaning the multi-owner scoping logic deliberately refuses to fall back to another user's mailboxes.

Source

Thrown at mcp_servers/email_server.py:318

        "smtp_ssl": os.environ.get("SMTP_SSL", "true").lower() == "true",
        "from_address": os.environ.get("EMAIL_FROM", ""),
        "archive_folder": os.environ.get("ARCHIVE_FOLDER", "Archive"),
        "trash_folder": os.environ.get("TRASH_FOLDER", "Trash"),
        "cache_db": os.environ.get(
            "EMAIL_CACHE_DB",
            EMAIL_CACHE_DB,
        ),
        "account_id": None,
        "account_name": None,
    }

    raw_rows = _read_accounts_from_db()
    if _mcp_owner_required(raw_rows):
        raise ValueError(_OWNER_SCOPE_ERROR)
    rows = _filter_accounts_for_owner(raw_rows)
    row = _resolve_account_from_rows(rows, account)
    if _current_owner() and raw_rows and not rows:
        raise ValueError("No email account is configured for the authenticated owner")
    if account and rows and not row:
        available = ", ".join(
            f"{r.get('name') or r.get('imap_user')} <{r.get('imap_user') or r.get('from_address') or '?'}>"
            for r in rows
        )
        raise ValueError(f"Email account not found for selector {account!r}. Available accounts: {available}")
    if row:
        cfg["account_id"] = row["id"]
        cfg["account_name"] = row["name"]
        cfg["imap_host"] = row["imap_host"] or cfg["imap_host"]
        cfg["imap_port"] = int(row["imap_port"] or cfg["imap_port"])
        cfg["imap_user"] = row["imap_user"] or cfg["imap_user"]
        # Passwords in email_accounts are stored encrypted via
        # src.secret_storage.encrypt — decrypt before handing to IMAP
        # (same path email_helpers.py:369 uses). Falling back to the raw
        # ciphertext is what produced AUTHENTICATIONFAILED previously.
        try:
            from src.secret_storage import decrypt as _decrypt

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Verify which owner the MCP session authenticates as and add/configure an email account row whose owner column matches it exactly
  2. Inspect EMAIL_CACHE_DB rows (owner column) and fix stale owner values or re-create the account via the account-management UI/API under the correct user
  3. If single-user, clear the MCP owner context so owner scoping is not applied

Example fix

-- accounts table before
SELECT id, name, owner FROM email_accounts;
-- rows have owner='admin' but MCP authenticates as 'beagle'
UPDATE email_accounts SET owner='beagle' WHERE owner='admin';
Defensive patterns

Strategy: try-catch

Validate before calling

rows = _read_accounts_from_db()
mine = _filter_accounts_for_owner(rows)
if _current_owner() and rows and not mine:
    raise SystemExit('Configure an email account for the current owner first')

Type guard

def owner_has_account(rows: list[dict], owner: str | None) -> bool:
    return not owner or any(r.get('owner') in (None, owner) for r in rows)

Try / catch

try:
    cfg = _load_config(account)
except ValueError as e:
    if 'No email account is configured' in str(e):
        # onboard an account for this owner, then retry once
        raise
    raise

Prevention

When it happens

Trigger: Calling any email MCP tool (list_emails, send, etc.) while MCP owner auth is active, accounts exist in the email accounts DB, but none of them have owner set to the current authenticated owner.

Common situations: Fresh deployment where accounts were seeded under a different/default owner string; owner identifier mismatch (email vs username casing); migrating a single-user setup to multi-owner without re-tagging existing email_accounts rows.

Understand the failure class

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/7a9d50c0cd588be8. Report an issue: GitHub.