BigPizzaV3/CodexPlusPlus · warning · SystemExit
登录超时。
Error message
登录超时。
What it means
qr_login only polls for a fixed duration (args.timeout). If the QR code is neither confirmed nor expired within that window, the polling loop ends and the tool raises SystemExit("\n登录超时。") — the login attempt is abandoned rather than retried indefinitely. It is the catch-all terminal failure of the QR login flow.
Solutions
- Re-run `codex-wechat login` with a larger --timeout (e.g. --timeout 300) and scan promptly.
- Check network connectivity / proxy settings to the iLink base URL; silent poll failures can make the loop run out without status transitions.
- Add logging of each poll's status_resp to see whether the server is returning unknown statuses that neither match confirmed nor expired.
- Verify the iLink endpoint is up (curl the base URL) before retrying login.
Example fix
// before qr_login(args.output, args.bot_type, args.timeout) // after qr_login(args.output, args.bot_type, max(args.timeout, 300)) # allow enough time to scan
Defensive patterns
Strategy: retry
Validate before calling
# ensure the endpoint is reachable before starting the poll loop import urllib.request urllib.request.urlopen(ILINK_BASE_URL, timeout=5) # raises early if unreachable
Try / catch
try:
qr_login(output, bot_type, timeout)
except SystemExit as e:
if "登录超时" in str(e):
print("tip: scan within the timeout; rerun with a larger --timeout") Prevention
- Pass a generous --timeout (>=300s).
- Verify network/proxy access to the iLink base URL before running login.
- Log every poll response so silent failures are visible.
- Avoid leaving the QR code unscanned while doing something else.
When it happens
Trigger: The `for` polling loop in qr_login completes its iterations (elapsed time > timeout) without ever seeing status "confirmed" or "expired"; raised via `raise SystemExit("\n登录超时。")` after the loop, reached from `main` with `qr_login(args.output, args.bot_type, args.timeout)`.
Common situations: User never scanned the QR code within the timeout; a very short --timeout value was passed; polls silently failing (network errors swallowed, status never matching confirmed/expired) so the loop spins until timeout; iLink endpoint down or returning unexpected statuses.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/f11a7868430c5262.
Report an issue: GitHub.
Appendix: source
Thrown at tools/codex-wechat/codex_wechat.py:813
base_url = str(status_resp.get("baseurl") or current_base or ILINK_BASE_URL)
user_id = str(status_resp.get("ilink_user_id") or "")
if not token or not account_id:
raise SystemExit("扫码成功但没有拿到 bot_token/account_id。")
config = load_json_file(output) if output.exists() else {}
config.update({"base_url": base_url, "token": token, "account_id": account_id})
if user_id:
config["login_user_id"] = user_id
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(config, ensure_ascii=False, indent=2) + "\n", "utf-8")
print(f"\n登录成功,配置已写入:{output}")
print("注意:该文件包含微信 iLink token,不要提交到 Git。")
return
elif status == "expired":
raise SystemExit("\n二维码已过期,请重新运行 login。")
time.sleep(1)
raise SystemExit("\n登录超时。")
def main(argv: list[str]) -> int:
parser = argparse.ArgumentParser(description="Codex WeChat Claw PoC")
sub = parser.add_subparsers(dest="command", required=True)
run_parser = sub.add_parser("run", help="启动 iLink -> codex app-server/exec -> iLink 转发")
run_parser.add_argument("--config", required=True, type=Path, help="配置文件路径")
login_parser = sub.add_parser("login", help="扫码获取 iLink token 并写入配置")
login_parser.add_argument("--output", required=True, type=Path, help="要写入的本地配置文件")
login_parser.add_argument("--bot-type", default="3", help="iLink bot_type,默认 3")
login_parser.add_argument("--timeout", default=480, type=int, help="扫码超时时间,秒")
args = parser.parse_args(argv)
if args.command == "run":
poll_loop(load_config(args.config))
return 0View on GitHub (pinned to b1ed92e5e4)