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

  1. Re-run `codex-wechat login` with a larger --timeout (e.g. --timeout 300) and scan promptly.
  2. Check network connectivity / proxy settings to the iLink base URL; silent poll failures can make the loop run out without status transitions.
  3. Add logging of each poll's status_resp to see whether the server is returning unknown statuses that neither match confirmed nor expired.
  4. 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

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 0

View on GitHub (pinned to b1ed92e5e4)