{"record":{"id":"63e5cdf2d1843026","repo":"sipeed/picoclaw","slug":"login-timeout","errorCode":null,"errorMessage":"login timeout","messagePattern":"login timeout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/channels/weixin/auth.go","lineNumber":78,"sourceCode":"\t\tHalfBlocks: true,\n\t}\n\tqrterminal.GenerateWithConfig(qrResp.QrcodeImgContent, qrconfig)\n\n\tfmt.Printf(\"\\nQR Code Link: %s\\n\\n\", qrResp.QrcodeImgContent)\n\tfmt.Println(\"Waiting for scan...\")\n\n\ttimeoutCtx, cancel := context.WithTimeout(ctx, opts.Timeout)\n\tdefer cancel()\n\n\tpollTicker := time.NewTicker(2 * time.Second)\n\tdefer pollTicker.Stop()\n\n\tscannedPrinted := false\n\n\tfor {\n\t\tselect {\n\t\tcase <-timeoutCtx.Done():\n\t\t\treturn \"\", \"\", \"\", \"\", fmt.Errorf(\"login timeout\")\n\t\tcase <-pollTicker.C:\n\t\t\tstatusResp, err := pollAPI.GetQRCodeStatus(timeoutCtx, qrResp.Qrcode)\n\t\t\tif err != nil {\n\t\t\t\t// Long poll timeout or temporary error\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch statusResp.Status {\n\t\t\tcase \"wait\":\n\t\t\t\t// still waiting\n\t\t\tcase \"scaned\":\n\t\t\t\tif !scannedPrinted {\n\t\t\t\t\tfmt.Println(\"👀 QR Code scanned! Please confirm login on your WeChat app...\")\n\t\t\t\t\tscannedPrinted = true\n\t\t\t\t}\n\t\t\tcase \"confirmed\":\n\t\t\t\tif statusResp.BotToken == \"\" || statusResp.IlinkBotID == \"\" {\n\t\t\t\t\treturn \"\", \"\", \"\", \"\", fmt.Errorf(\"login confirmed but missing bot_token or ilink_bot_id\")","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/weixin/auth.go#L60-L96","documentation":"The Weixin QR login poller hit its deadline: within opts.Timeout (default 5 minutes) the QR code never reached the \"confirmed\" state. The loop polls GetQRCodeStatus every 2 seconds; the timeout context fires while status is still \"wait\"/\"scaned\", or while poll requests keep erroring (poll errors are swallowed with continue, so a persistently failing poll also ends here).","triggerScenarios":"Starting interactive login and not scanning+confirming the QR in time; network to the polling endpoint failing for the whole window so every status poll errors and loops; the user scanned but never tapped confirm on the phone; the QR expired (status \"expired\" would normally return error 672 first, but a missed final poll can time out instead).","commonSituations":"Unattended login attempt (nobody scans); user distracted during the 5-minute window; flaky network dropping every poll; timeout lowered for tests; slow phone confirmation on a large window where the last poll raced the timer.","solutions":["Simply re-run the login command and scan the new QR promptly","Raise opts.Timeout (e.g. 10*time.Minute) if the operator needs more time","Make sure the host running login has stable network access to the polling endpoint so polls are not silently skipped","Watch the console output: \"QR Code scanned\" means confirm on the phone is the missing step"],"exampleFix":"// before\nopts := weixin.LoginOpts{Timeout: 5 * time.Minute}\n\n// after\nopts := weixin.LoginOpts{Timeout: 10 * time.Minute}","handlingStrategy":"retry","validationCode":"// assert a sane timeout and an operator before starting\nif opts.Timeout < 2*time.Minute {\n    opts.Timeout = 5 * time.Minute\n}\nif !operatorReadyToScan() {\n    return errors.New(\"no operator present; aborting login\")\n}","typeGuard":"func isWeixinLoginTimeout(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"login timeout\")\n}","tryCatchPattern":"if _, _, _, _, err := weixin.Login(ctx, opts); err != nil {\n    if isWeixinLoginTimeout(err) {\n        // benign: re-run with a fresh QR; consider a longer Timeout\n        opts.Timeout *= 2\n        _, _, _, _, err = weixin.Login(ctx, opts)\n    }\n}","preventionTips":["Start the login command only when someone is ready to scan","Set Timeout generously (10m) for slow operators","Watch console cues: 'QR Code scanned' means the phone confirm step is pending","Ensure stable network so polls are not silently swallowed until the deadline"],"tags":["weixin","auth","login","timeout","qrcode"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}