datawhalechina/hello-agents · error · ValueError
未配置邮箱信息
Error message
未配置邮箱信息
What it means
Raised by _fetch_via_imap() in the EmailDigestAgent notebook when _read_imap_config() returns an empty username or password. _read_imap_config() resolves credentials from explicit arguments, then an 'imap' section of a config file, falling back to empty strings (username="", password="") — it never raises for missing config, so the blank credentials surface here as ValueError('未配置邮箱信息'). It is a configuration error, not an IMAP protocol failure.
Source
Thrown at Co-creation-projects/Johnx-w-EmailDigestAgent/main.ipynb:288
" username = os.getenv(\"IMAP_USERNAME\") or \"\"\n",
" password = os.getenv(\"IMAP_PASSWORD\") or \"\"\n",
" if not (username and password and server):\n",
" try:\n",
" with open(\"config/email_config.json\", \"r\", encoding=\"utf-8\") as f:\n",
" cfg = json.load(f)\n",
" imap_cfg = cfg.get(\"imap\", {})\n",
" server = server or imap_cfg.get(\"server\", \"imap.qq.com\")\n",
" port = port or imap_cfg.get(\"port\", 993)\n",
" username = username or imap_cfg.get(\"username\", \"\")\n",
" password = password or imap_cfg.get(\"password\", \"\")\n",
" except FileNotFoundError:\n",
" pass\n",
" return server or \"imap.qq.com\", port or 993, username, password\n",
"\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",View on GitHub (pinned to 606a07d341)
Solutions
- Create the config file the notebook expects with an 'imap' section containing server/port/username/password keys, matching the exact key names read by _read_imap_config().
- For QQ Mail, enable IMAP in mailbox settings and use the 16-character authorization code (授权码) as the password, not the login password.
- Alternatively pass username= and password= directly to the tool/constructor call so they don't depend on file config.
- If you just want to see the agent run, set use_demo=True to use demo_emails and skip IMAP entirely.
- Verify the config file is being found: the code silently ignores FileNotFoundError, so confirm the exact path _read_imap_config() opens and that the notebook cwd contains it.
Example fix
// before (config missing -> silent empty credentials)
try:
cfg = json.loads(cfg_path.read_text())
imap_cfg = cfg.get("imap", {})
username = username or imap_cfg.get("username", "")
password = password or imap_cfg.get("password", "")
except FileNotFoundError:
pass # silently proceeds, later raises 未配置邮箱信息
// after (fail early with actionable message)
try:
cfg = json.loads(cfg_path.read_text())
except FileNotFoundError:
raise ValueError(f"未找到配置文件 {cfg_path},请复制模板并填写 imap 节点") from None
imap_cfg = cfg.get("imap", {})
username = username or imap_cfg.get("username", "")
password = password or imap_cfg.get("password", "") Defensive patterns
Strategy: validation
Validate before calling
def has_imap_credentials(username: str, password: str, cfg_path: Path) -> bool:
if username and password:
return True
if not cfg_path.exists():
return False
imap_cfg = json.loads(cfg_path.read_text()).get("imap", {})
return bool(imap_cfg.get("username") and imap_cfg.get("password"))
# before agent.run(use_demo=False):
if not has_imap_credentials(username, password, cfg_path):
raise SystemExit("缺少 IMAP 凭据:请在配置文件 imap 节点填写 username/password,或改用 use_demo=True") Try / catch
try:
result = agent.run(hours=24)
except ValueError as e:
if "未配置邮箱信息" in str(e):
# config problem: point user at the config file, never retry
print(f"配置缺失: {e} — 请填写 imap username/password")
raise Prevention
- Copy the config template and fill the imap section before the first live run.
- Use provider-specific IMAP authorization codes (QQ/163 授权码), not the login password.
- Fail fast in _read_imap_config() on FileNotFoundError instead of returning empty strings.
- Ship a startup check that validates credentials exist (non-empty) and logs which key is missing.
When it happens
Trigger: Calling agent.run() with use_demo=False (or instantiating the email tool in live mode) when: no username/password arguments were passed, and the config file (e.g. config.json/.yaml read inside _read_imap_config) does not exist (FileNotFoundError is swallowed by 'except FileNotFoundError: pass') or its imap section lacks username/password keys. Defaults only cover server ('imap.qq.com') and port (993), never credentials.
Common situations: Fresh clone of the project without copying the config template; config file present but keys named differently (e.g. 'user'/'pass' instead of 'username'/'password'); QQ Mail users forgetting to generate an authorization code (QQ Mail requires an app-specific IMAP code, not the account password); running the notebook from a directory where the relative config path doesn't resolve.
Related errors
- 无法选择 INBOX (状态: {select_status!r})
- IMAP 连接失败: {e}
- TMDB 未配置:请在 .env 设置 TMDB_ACCESS_TOKEN 或 TMDB_API_KEY
- TAVILY_API_KEY is required for TavilySearchTool
- 未配置 AMiner API Key。请前往 https://open.aminer.cn/ 注册获取,然后在 .env
AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14).
Data as JSON: /api/errors/62ca9395f1bd201b.
Report an issue: GitHub.