datawhalechina/hello-agents · error · ValueError

无法选择 INBOX (状态: {select_status!r})

Error message

无法选择 INBOX (状态: {select_status!r})

What it means

Raised in _fetch_via_imap() after login succeeds but mail.select() never returns 'OK' for any of the three attempted mailbox selectors: 'INBOX', '\"INBOX\"' (quoted), and 'INBOX' with readonly=True. The f-string in the raise is missing the .format/format-evaluation context issue — note the source uses f-string syntax so select_status is interpolated, showing the raw IMAP status (e.g. 'NO' or 'BAD'). Select failure at this point is almost always a server-side or protocol-level rejection of the SELECT command, not an auth problem (login already succeeded).

Source

Thrown at Co-creation-projects/Johnx-w-EmailDigestAgent/main.ipynb:302

    "\n",
    "    def _fetch_via_imap(self, max_emails: int = 50, hours: int = 24) -> list:\n",
    "        server, port, username, password = self._read_imap_config()\n",
    "        if not username or not password:\n",
    "            raise ValueError(\"未配置邮箱信息\")\n",
    "\n",
    "        console.print(f\"[dim]  连接 {server}:{port}  用户: {username}...[/dim]\")\n",
    "        mail = imaplib.IMAP4_SSL(server, port)\n",
    "        mail.login(username, password)\n",
    "\n",
    "        # 选择收件箱 — 依次尝试: 无引号, 带引号, readonly\n",
    "        select_status = None\n",
    "        for name, rdonly in [(\"INBOX\", False), ('\"INBOX\"', False), (\"INBOX\", True)]:\n",
    "            select_status, select_data = mail.select(name, readonly=rdonly)\n",
    "            console.print(f\"[dim]  select({name!r}, readonly={rdonly}) -> {select_status!r}[/dim]\")\n",
    "            if select_status == \"OK\":\n",
    "                break\n",
    "        if select_status != \"OK\":\n",
    "            raise ValueError(f\"无法选择 INBOX (状态: {select_status!r})\")\n",
    "\n",
    "        since_date = (datetime.now() - timedelta(hours=hours)).strftime(\"%d-%b-%Y\")\n",
    "        search_criteria = f'(UNSEEN SINCE {since_date})'\n",
    "        status, message_ids = mail.search(None, search_criteria)\n",
    "        if status != \"OK\":\n",
    "            mail.logout()\n",
    "            return []\n",
    "\n",
    "        ids = message_ids[0].split()\n",
    "        ids = list(reversed(ids))[:max_emails]\n",
    "        emails = []\n",
    "        for msg_id in ids:\n",
    "            try:\n",
    "                status, msg_data = mail.fetch(msg_id, \"(RFC822)\")\n",
    "                if status != \"OK\":\n",
    "                    continue\n",
    "                raw_email = msg_data[0][1]\n",
    "                msg = email.message_from_bytes(raw_email)\n",

View on GitHub (pinned to 606a07d341)

Solutions

  1. Log in to the mailbox web settings and confirm the IMAP/SMTP service is enabled (QQ Mail: 设置→账户→开启IMAP; Gmail: Settings→Forwarding and POP/IMAP→Enable IMAP).
  2. Inspect the printed debug line 'select(...) -> status' — a 'NO' usually includes a server message (visible in mail.error or the last response) indicating permission vs. nonexistent mailbox.
  3. List the actual mailboxes once with typ, data = mail.list() and SELECT the exact name the server reports instead of assuming 'INBOX'.
  4. If the server rejects read-write SELECT (e.g. shared/audited mailboxes), keep only the ('INBOX', True) readonly attempt.
  5. Retry after a minute if the provider throttles rapid successive sessions; close prior connections with mail.logout() in a finally block.

Example fix

// before
for name, rdonly in [("INBOX", False), ('"INBOX"', False), ("INBOX", True)]:
    select_status, select_data = mail.select(name, readonly=rdonly)
    if select_status == "OK":
        break
if select_status != "OK":
    raise ValueError(f"无法选择 INBOX (状态: {select_status!r})")

// after (discover the real mailbox name, prefer readonly)
typ, boxes = mail.list()
if typ != "OK":
    raise ValueError(f"LIST 失败: {boxes!r}")
inbox = next((b.decode().rsplit('"', 2)[-2] for b in boxes if b.decode().upper().endswith('INBOX')), "INBOX")
select_status, select_data = mail.select(inbox, readonly=True)
if select_status != "OK":
    raise ValueError(f"无法选择 {inbox} (状态: {select_status!r}, 响应: {select_data!r})")
Defensive patterns

Strategy: fallback

Validate before calling

def select_inbox(mail: imaplib.IMAP4_SSL) -> None:
    typ, boxes = mail.list()
    names = [b.decode().rsplit('"', 2)[-2] for b in (boxes or []) if b]
    for candidate in [n for n in names if n.upper().endswith("INBOX")] + ["INBOX"]:
        status, _ = mail.select(candidate, readonly=True)
        if status == "OK":
            return
    raise ValueError(f"无法选择 INBOX,服务器邮箱列表: {names}")

Try / catch

try:
    select_status, select_data = mail.select("INBOX", readonly=True)
except imaplib.IMAP4.abort as e:
    # protocol/stream broken: reconnect once, then give up
    raise RuntimeError(f"连接中断,请重试: {e}") from e
if select_status != "OK":
    print(f"SELECT 失败: {select_data!r} — 请检查邮箱 IMAP 开关")
    raise ValueError(f"无法选择 INBOX (状态: {select_status!r})")

Prevention

When it happens

Trigger: mail.select(name) returning status 'NO'/'BAD' for all three attempts: server rejects SELECT because the mailbox name is wrong or IMAP4rev1 namespace differs; server in a broken/quota-full state; some servers return non-OK to a read-write SELECT on a read-only mailbox, which the third (readonly=True) attempt usually covers; proxy/SSL middleboxes corrupting the command; Gmail accounts with IMAP disabled in settings also fail at SELECT/EXAMINE stage.

Common situations: QQ/163 mail with IMAP service not enabled in web settings; Gmail with IMAP disabled under Forwarding and POP/IMAP; mailbox name localized or namespaced (e.g. 'INBOX' not exposed, or server requires '"INBOX"' quoting); server temporary failure after too many rapid SELECT connections; antivirus/firewall mangling the TLS stream causing protocol desync.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/e67628d4570b3a08. Report an issue: GitHub.