{"record":{"id":"eafbca3f7f36679e","repo":"BigPizzaV3/CodexPlusPlus","slug":"bot-token-account-id","errorCode":null,"errorMessage":"扫码成功但没有拿到 bot_token/account_id。","messagePattern":"扫码成功但没有拿到 bot_token/account_id。","errorType":"exception","errorClass":"SystemExit","httpStatus":null,"severity":"error","filePath":"tools/codex-wechat/codex_wechat.py","lineNumber":798,"sourceCode":"            timeout=35,\n        )\n        status = str(status_resp.get(\"status\") or \"wait\")\n        if status == \"wait\":\n            print(\".\", end=\"\", flush=True)\n        elif status == \"scaned\":\n            print(\"\\n已扫码，请在微信中确认。\")\n        elif status == \"scaned_but_redirect\":\n            redirect_host = str(status_resp.get(\"redirect_host\") or \"\")\n            if redirect_host:\n                current_base = \"https://\" + redirect_host\n                print(f\"\\n切换 iLink base_url：{current_base}\")\n        elif status == \"confirmed\":\n            token = str(status_resp.get(\"bot_token\") or \"\")\n            account_id = str(status_resp.get(\"ilink_bot_id\") or \"\")\n            base_url = str(status_resp.get(\"baseurl\") or current_base or ILINK_BASE_URL)\n            user_id = str(status_resp.get(\"ilink_user_id\") or \"\")\n            if not token or not account_id:\n                raise SystemExit(\"扫码成功但没有拿到 bot_token/account_id。\")\n\n            config = load_json_file(output) if output.exists() else {}\n            config.update({\"base_url\": base_url, \"token\": token, \"account_id\": account_id})\n            if user_id:\n                config[\"login_user_id\"] = user_id\n            output.parent.mkdir(parents=True, exist_ok=True)\n            output.write_text(json.dumps(config, ensure_ascii=False, indent=2) + \"\\n\", \"utf-8\")\n            print(f\"\\n登录成功，配置已写入：{output}\")\n            print(\"注意：该文件包含微信 iLink token，不要提交到 Git。\")\n            return\n        elif status == \"expired\":\n            raise SystemExit(\"\\n二维码已过期，请重新运行 login。\")\n        time.sleep(1)\n\n    raise SystemExit(\"\\n登录超时。\")\n\n\ndef main(argv: list[str]) -> int:","sourceCodeStart":780,"sourceCodeEnd":816,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/tools/codex-wechat/codex_wechat.py#L780-L816","documentation":"qr_login polls the WeChat iLink login status endpoint after the QR code is scanned and confirmed. When the confirmed response lacks a non-empty bot_token or ilink_bot_id (account_id), the tool cannot write a usable login config, so it raises SystemExit. This guards against saving a config file that would fail later during run.","triggerScenarios":"The status endpoint returns status == \"confirmed\" but status_resp.get(\"bot_token\") or status_resp.get(\"ilink_bot_id\") is missing, None, or an empty string. Raised via `raise SystemExit(\"扫码成功但没有拿到 bot_token/account_id。\")` inside qr_login, called from `main` with the login subcommand.","commonSituations":"WeChat iLink server-side issues where the confirmation response omits credentials; an API version change renaming bot_token/ilink_bot_id fields; a proxy or modified baseurl (ILINK_BASE_URL override) returning an unexpected response shape; partial bot registration on the WeChat side so the bot exists but no token was issued.","solutions":["Re-run `codex-wechat login` — the failure is often transient on the iLink side; a fresh QR login usually returns the token.","Print/inspect the raw status_resp (add a debug dump before the raise) to see which fields the server actually returned and whether field names changed.","Verify the baseurl being used (status_resp baseurl vs current_base vs ILINK_BASE_URL); a wrong or outdated ILINK_BASE_URL can hit an endpoint that returns a different response shape.","Check whether the WeChat iLink bot API updated field names (e.g. token vs bot_token) and patch the extraction in qr_login accordingly.","Confirm the bot was fully registered/approved on the WeChat side; an unprovisioned bot can be confirmed without a token."],"exampleFix":"// before\nif not token or not account_id:\n    raise SystemExit(\"扫码成功但没有拿到 bot_token/account_id。\")\n// after\nif not token or not account_id:\n    print(f\"debug status_resp={status_resp!r}\")  # inspect actual payload\n    raise SystemExit(\"扫码成功但没有拿到 bot_token/account_id。\")","handlingStrategy":"validation","validationCode":"# before trusting the confirmed response\nresp = poll_status(...)\nif resp.get(\"status\") == \"confirmed\":\n    if not resp.get(\"bot_token\") or not resp.get(\"ilink_bot_id\"):\n        raise RuntimeError(f\"confirmed response missing credentials: {resp}\")","typeGuard":"def has_credentials(resp: dict) -> bool:\n    return bool(resp.get(\"bot_token\")) and bool(resp.get(\"ilink_bot_id\"))","tryCatchPattern":null,"preventionTips":["Log the full status payload on every poll so missing fields are diagnosable.","Pin/verify the iLink API version and field names in one constants module.","Test qr_login against a stub server that returns confirmed-without-token to keep the guard exercised."],"tags":["authentication","login","missing-credentials","unexpected-response-shape"],"backgroundTag":"missing-credentials","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}