{"record":{"id":"2b92c756dc5875e7","repo":"datawhalechina/hello-agents","slug":"imap-e","errorCode":null,"errorMessage":"IMAP 连接失败: {e}","messagePattern":"IMAP 连接失败: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/Johnx-w-EmailDigestAgent/main.ipynb","lineNumber":345,"sourceCode":"    \"                    \\\"body\\\": _parse_email_body(msg)\\n\",\n    \"                })\\n\",\n    \"            except Exception as e:\\n\",\n    \"                console.print(f\\\"[yellow]  ⚠️ 邮件解析失败: {e}[/yellow]\\\")\\n\",\n    \"                continue\\n\",\n    \"\\n\",\n    \"        mail.logout()\\n\",\n    \"        console.print(f\\\"[dim]  已断开连接[/dim]\\\")\\n\",\n    \"        return emails\\n\",\n    \"\\n\",\n    \"    def run(self, hours: int = 24, max_emails: int = 50) -> str:\\n\",\n    \"        if self.use_demo:\\n\",\n    \"            emails = self.demo_emails[:max_emails]\\n\",\n    \"        else:\\n\",\n    \"            try:\\n\",\n    \"                emails = self._fetch_via_imap(max_emails=max_emails, hours=hours)\\n\",\n    \"            except imaplib.IMAP4.error as e:\\n\",\n    \"                console.print(f\\\"[red]❌ IMAP 连接失败: {e}[/red]\\\")\\n\",\n    \"                raise RuntimeError(f\\\"IMAP 连接失败: {e}\\\") from e\\n\",\n    \"            except ValueError:\\n\",\n    \"                raise\\n\",\n    \"            except Exception as e:\\n\",\n    \"                console.print(f\\\"[red]❌ 未知错误: {e}[/red]\\\")\\n\",\n    \"                raise\\n\",\n    \"\\n\",\n    \"        if not emails:\\n\",\n    \"            return json.dumps({\\\"message\\\": \\\"没有新的未读邮件\\\", \\\"emails\\\": []}, ensure_ascii=False, indent=2)\\n\",\n    \"        return json.dumps({\\\"count\\\": len(emails), \\\"fetch_time\\\": datetime.now().strftime(\\\"%Y-%m-%d %H:%M:%S\\\"), \\\"emails\\\": emails}, ensure_ascii=False, indent=2)\\n\",\n    \"\\n\",\n    \"    def get_parameters(self):\\n\",\n    \"        from hello_agents.tools import ToolParameter\\n\",\n    \"        return []\\n\",\n    \"\\n\",\n    \"print(\\\"✅ EmailFetchTool defined (IMAP version)\\\")\"\n   ]\n  },\n  {","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/Johnx-w-EmailDigestAgent/main.ipynb#L327-L363","documentation":"RuntimeError('IMAP 连接失败: {e}') is the wrapper run() raises after catching imaplib.IMAP4.error in _fetch_via_imap(); the original exception is chained via 'raise ... from e'. imaplib.IMAP4.error covers all IMAP-level failures: authentication rejection (login errors are IMAP4.error subclasses), command aborts, and connection-timeout variants (IMAP4.abort). Note the f-string is literal in this path only if the source lacks the f prefix — per the source it is f-stringed, so the underlying message is embedded. Because login() failures raise IMAP4.error, wrong credentials typically surface here.","triggerScenarios":"agent.run(hours, max_emails) in live mode (use_demo=False) when _fetch_via_imap() raises imaplib.IMAP4.error: mail.login() rejected (bad password / missing authorization code / IMAP disabled for the account), IMAP4.abort from a dropped socket mid-session, or server returning BAD to a command. The except chain deliberately re-raises ValueError (e.g. 未配置邮箱信息) untouched and wraps only IMAP4.error.","commonSituations":"Using the account password instead of the provider's IMAP authorization code (QQ/163); IMAP not enabled server-side; firewall blocking port 993 outbound; server-side rate limiting after repeated runs; token/session expired for OAuth-based providers.","solutions":["Read the wrapped message: 'IMAP 连接失败: {e}' includes the server's reason — '[AUTHENTICATIONFAILED]' means credentials, 'abort' means the socket died.","For Chinese providers (QQ/163/126), generate and use the IMAP 授权码 as password and confirm IMAP service is switched on in webmail settings.","Verify network reachability of server:993 (e.g. openssl s_client -connect imap.qq.com:993 or a socket test) from the notebook host.","Distinguish auth vs. transient failure: catch imaplib.IMAP4.abort separately and retry once with backoff; do not retry AUTHENTICATIONFAILED.","For a smoke test without valid credentials, run with use_demo=True."],"exampleFix":"// before\ntry:\n    emails = self._fetch_via_imap(max_emails=max_emails, hours=hours)\nexcept imaplib.IMAP4.error as e:\n    console.print(f\"[red]❌ IMAP 连接失败: {e}[/red]\")\n    raise RuntimeError(f\"IMAP 连接失败: {e}\") from e\n\n// after (classify abort vs auth failure, retry only transient)\ntry:\n    emails = self._fetch_via_imap(max_emails=max_emails, hours=hours)\nexcept imaplib.IMAP4.abort as e:\n    time.sleep(2)\n    emails = self._fetch_via_imap(max_emails=max_emails, hours=hours)  # one retry\nexcept imaplib.IMAP4.error as e:\n    raise RuntimeError(f\"IMAP 连接失败（请检查授权码/IMAP开关）: {e}\") from e","handlingStrategy":"try-catch","validationCode":"import socket, ssl\n\ndef imap_endpoint_reachable(server: str, port: int, timeout: float = 5.0) -> bool:\n    try:\n        with socket.create_connection((server, port), timeout=timeout):\n            return True\n    except OSError:\n        return False\n\n# before run():\nif not imap_endpoint_reachable(server, 993):\n    raise SystemExit(f\"无法连接 {server}:993，请检查网络/防火墙\")","typeGuard":null,"tryCatchPattern":"try:\n    result = agent.run(hours=24, max_emails=50)\nexcept RuntimeError as e:\n    msg = str(e)\n    if \"AUTHENTICATIONFAILED\" in msg or \"LOGIN\" in msg.upper():\n        # credential problem: fix config, do not retry\n        print(\"凭据错误：请更新 IMAP 授权码\")\n    elif \"abort\" in msg.lower():\n        # transient: safe to retry once after a short delay\n        time.sleep(3)\n        result = agent.run(hours=24, max_emails=50)\n    else:\n        raise","preventionTips":["Store the provider's IMAP authorization code in the config, never the webmail password.","Separate imaplib.IMAP4.abort (retryable) from imaplib.IMAP4.error (fatal) in the except chain.","Pre-flight check TCP reachability of server:993 before invoking run().","Chain the original exception ('from e') — already done here — and log the server's message text for diagnosis."],"tags":["imap","email","authentication","network","python"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}