odysseus-dev/odysseus · error · ValueError
IMAP folder not found: {folder}
Error message
IMAP folder not found: {folder} What it means
Raised when conn.select(folder, readonly=True) returns a status other than 'OK', meaning the IMAP server refused the folder name. Python's imaplib does not throw for a missing mailbox; it returns 'NO', so this ValueError converts that into an explicit error naming the folder.
Source
Thrown at mcp_servers/email_server.py:984
# ── Tool implementations ──
def _list_emails(folder="INBOX", max_results=20, unresponded_only=False,
unread_only=False, account=None):
"""List emails newest-first. By default returns the latest messages,
including read mail, so it matches normal inbox UI expectations.
Pass unread_only=True and/or unresponded_only=True for attention scans.
account selects mailbox (None = default).
"""
fixture = _fixture_list_emails(folder, max_results, unresponded_only, unread_only, account)
if fixture is not None:
return fixture
conn = None
try:
conn = _imap_connect(account)
select_status, _ = conn.select(_q(folder), readonly=True)
if select_status != "OK":
raise ValueError(f"IMAP folder not found: {folder}")
if unread_only and unresponded_only:
status, data = conn.uid("SEARCH", None, "(UNSEEN UNANSWERED)")
elif unread_only:
status, data = conn.uid("SEARCH", None, "(UNSEEN)")
elif unresponded_only:
# Was missing — unresponded_only=True (without unread_only) fell through
# to "ALL" and returned answered mail too, despite the documented
# "emails without replies" behaviour.
status, data = conn.uid("SEARCH", None, "(UNANSWERED)")
else:
# Include read too — IMAP search "ALL" returns the entire folder
status, data = conn.uid("SEARCH", None, "ALL")
if status != "OK" or not data[0]:
return []
uid_list = list(reversed(data[0].split()))[:max_results]View on GitHub (pinned to f9235ebbf1)
Solutions
- List the server's actual folders first (imaplib list() or the MCP server's folder-list tool) and use the exact returned name
- Use 'INBOX', which exists on every IMAP server
- Match folder-name case and any delimiters (e.g. '[Gmail]/...') exactly
Example fix
# before
conn.select('Inbox', readonly=True) # NO on case-sensitive servers
# after
status, folders = conn.list()
conn.select('INBOX', readonly=True) Defensive patterns
Strategy: validation
Validate before calling
typ, data = conn.list()
names = [d.decode().rsplit('"',2)[-2] for d in data]
folder = folder if folder in names else 'INBOX' Type guard
def folder_exists(conn, folder: str) -> bool:
typ, data = conn.list()
return any(folder.encode() in (d or b'') for d in data) Try / catch
try:
conn.select(_q(folder), readonly=True)
except ValueError:
folder = 'INBOX'
conn.select('INBOX', readonly=True) Prevention
- Cache the folder list per account and refresh on this error
- Use exact provider folder strings (Gmail needs '[Gmail]/Sent Mail')
- Default to 'INBOX' when unsure
When it happens
Trigger: Calling list_emails with folder='Inbox' against a case-sensitive server that only has 'INBOX'; requesting an archived/nested folder like 'Archive/2024' that does not exist on the server; quoting issues in the folder name passed through _q().
Common situations: Folder naming differences between mail providers (Gmail '[Gmail]/Sent Mail' vs plain 'Sent'); hard-coded folder names ported from another provider; folders the IMAP account cannot see due to ACLs.
Related errors
- HTTP ${res.status}
- data.error || 'failed'
- data.error
- Cannot create reply: sender address is missing from this ema
- Cleanup failed
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/ea0d0d126fd3474f.
Report an issue: GitHub.