BigPizzaV3/CodexPlusPlus · warning · SystemExit

二维码已过期,请重新运行 login。

Error message

二维码已过期,请重新运行 login。

What it means

During qr_login polling, when the status endpoint reports "expired", the QR code is no longer valid and the login must be restarted from a fresh code. The tool raises SystemExit immediately instead of continuing to poll. QR codes have a server-enforced lifetime, so expiry is a normal, expected terminal state.

Solutions

  1. Re-run `codex-wechat login` to generate a fresh QR code and scan it promptly.
  2. Scan the QR code immediately after it is displayed; do not let it sit.
  3. If expiry happens consistently quickly, pass a longer --timeout so polling continues across multiple code lifecycles is not needed — instead just retry; check server-side expiry policy.
  4. Check network latency to the iLink endpoint; excessive delays can cause the poll to observe expired before confirmed.

Example fix

null
Defensive patterns

Strategy: retry

Validate before calling

# check code age before prompting the user to scan
if time.monotonic() - qr_created_at > QR_TTL_SECONDS:
    qr = regenerate_qr()  # refresh before it expires

Try / catch

try:
    qr_login(output, bot_type, timeout)
except SystemExit as e:
    if "已过期" in str(e):
        qr_login(output, bot_type, timeout)  # regenerate and retry once

Prevention

When it happens

Trigger: While polling in qr_login, a status response with status == "expired" is received before the loop returns (confirmed) or the timeout elapses. Raised from `raise SystemExit("\n二维码已过期,请重新运行 login。")`, called from `main` via the login subcommand.

Common situations: User waited too long before scanning (most QR codes expire in ~1–2 minutes); slow network/proxy causing polls to miss the confirmation window; user scanned after the code had already lapsed; running login unattended and returning to it late.

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/8b83ce5327d61d34. Report an issue: GitHub.

Appendix: source

Thrown at tools/codex-wechat/codex_wechat.py:810

        elif status == "confirmed":
            token = str(status_resp.get("bot_token") or "")
            account_id = str(status_resp.get("ilink_bot_id") or "")
            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)

View on GitHub (pinned to b1ed92e5e4)