datawhalechina/hello-agents · error · RuntimeError
IMAP 连接失败: {e}
Error message
IMAP 连接失败: {e} What it means
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.
Source
Thrown at Co-creation-projects/Johnx-w-EmailDigestAgent/main.ipynb:345
" \"body\": _parse_email_body(msg)\n",
" })\n",
" except Exception as e:\n",
" console.print(f\"[yellow] ⚠️ 邮件解析失败: {e}[/yellow]\")\n",
" continue\n",
"\n",
" mail.logout()\n",
" console.print(f\"[dim] 已断开连接[/dim]\")\n",
" return emails\n",
"\n",
" def run(self, hours: int = 24, max_emails: int = 50) -> str:\n",
" if self.use_demo:\n",
" emails = self.demo_emails[:max_emails]\n",
" else:\n",
" try:\n",
" emails = self._fetch_via_imap(max_emails=max_emails, hours=hours)\n",
" except imaplib.IMAP4.error as e:\n",
" console.print(f\"[red]❌ IMAP 连接失败: {e}[/red]\")\n",
" raise RuntimeError(f\"IMAP 连接失败: {e}\") from e\n",
" except ValueError:\n",
" raise\n",
" except Exception as e:\n",
" console.print(f\"[red]❌ 未知错误: {e}[/red]\")\n",
" raise\n",
"\n",
" if not emails:\n",
" return json.dumps({\"message\": \"没有新的未读邮件\", \"emails\": []}, ensure_ascii=False, indent=2)\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",
" def get_parameters(self):\n",
" from hello_agents.tools import ToolParameter\n",
" return []\n",
"\n",
"print(\"✅ EmailFetchTool defined (IMAP version)\")"
]
},
{View on GitHub (pinned to 606a07d341)
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.
Example fix
// before
try:
emails = self._fetch_via_imap(max_emails=max_emails, hours=hours)
except imaplib.IMAP4.error as e:
console.print(f"[red]❌ IMAP 连接失败: {e}[/red]")
raise RuntimeError(f"IMAP 连接失败: {e}") from e
// after (classify abort vs auth failure, retry only transient)
try:
emails = self._fetch_via_imap(max_emails=max_emails, hours=hours)
except imaplib.IMAP4.abort as e:
time.sleep(2)
emails = self._fetch_via_imap(max_emails=max_emails, hours=hours) # one retry
except imaplib.IMAP4.error as e:
raise RuntimeError(f"IMAP 连接失败(请检查授权码/IMAP开关): {e}") from e Defensive patterns
Strategy: try-catch
Validate before calling
import socket, ssl
def imap_endpoint_reachable(server: str, port: int, timeout: float = 5.0) -> bool:
try:
with socket.create_connection((server, port), timeout=timeout):
return True
except OSError:
return False
# before run():
if not imap_endpoint_reachable(server, 993):
raise SystemExit(f"无法连接 {server}:993,请检查网络/防火墙") Try / catch
try:
result = agent.run(hours=24, max_emails=50)
except RuntimeError as e:
msg = str(e)
if "AUTHENTICATIONFAILED" in msg or "LOGIN" in msg.upper():
# credential problem: fix config, do not retry
print("凭据错误:请更新 IMAP 授权码")
elif "abort" in msg.lower():
# transient: safe to retry once after a short delay
time.sleep(3)
result = agent.run(hours=24, max_emails=50)
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 未配置邮箱信息
- 无法选择 INBOX (状态: {select_status!r})
- 工具 '{tool_name}' 执行超时
- Hunter Agent执行失败: {str(e)}
- ArXiv API请求失败: {response.status}
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/2b92c756dc5875e7.
Report an issue: GitHub.