{"record":{"id":"ab736e68bfd51bde","repo":"sipeed/picoclaw","slug":"qrcode-expired-please-try-again","errorCode":null,"errorMessage":"qrcode expired, please try again","messagePattern":"qrcode expired, please try again","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/channels/weixin/auth.go","lineNumber":125,"sourceCode":"\t\t\t\t\t\t\"scaned_but_redirect received without redirect_host; continuing on current host\",\n\t\t\t\t\t)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tnextBaseURL := \"https://\" + statusResp.RedirectHost + \"/\"\n\t\t\t\tnextAPI, nextErr := NewApiClient(nextBaseURL, \"\", opts.Proxy)\n\t\t\t\tif nextErr != nil {\n\t\t\t\t\tlogger.WarnCF(\"weixin\", \"Failed to switch QR polling host\", map[string]any{\n\t\t\t\t\t\t\"redirect_host\": statusResp.RedirectHost,\n\t\t\t\t\t\t\"error\":         nextErr.Error(),\n\t\t\t\t\t})\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tpollAPI = nextAPI\n\t\t\t\tlogger.InfoCF(\"weixin\", \"Switched QR polling host\", map[string]any{\n\t\t\t\t\t\"redirect_host\": statusResp.RedirectHost,\n\t\t\t\t})\n\t\t\tcase \"expired\":\n\t\t\t\treturn \"\", \"\", \"\", \"\", fmt.Errorf(\"qrcode expired, please try again\")\n\t\t\tdefault:\n\t\t\t\tlogger.WarnCF(\"weixin\", \"Unknown QR code status\", map[string]any{\n\t\t\t\t\t\"status\": statusResp.Status,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n}\n","sourceCodeStart":107,"sourceCodeEnd":134,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/weixin/auth.go#L107-L134","documentation":"Terminal state of the Weixin QR login: the status poll returned \"expired\", meaning the QR code shown to the user is no longer valid (Weixin QR codes live roughly a couple of minutes). The flow deliberately stops instead of silently re-requesting, so the operator must restart the login to get a fresh code.","triggerScenarios":"Starting login, printing the QR, then not completing the scan+confirm within Weixin's QR validity window; also possible after a redirect to a new polling host (scaned_but_redirect path) consumed part of the window.","commonSituations":"Operator walks away after launching the login command; slow terminals/SSH sessions delaying when the QR is actually rendered; expired code discovered only when the user finally tries to scan; repeated expiries when login is scripted without a human ready.","solutions":["Re-run the login command — it fetches and prints a fresh QR code","Scan promptly after the QR renders; have WeChat ready before starting the command","If expiring repeatedly in automation, trigger login only when a human is ready to scan","Keep the terminal responsive (avoid slow SSH hops) so the code is visible immediately"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"null","typeGuard":"func isWeixinQRExpired(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"qrcode expired\")\n}","tryCatchPattern":"for {\n    _, _, _, _, err := weixin.Login(ctx, opts)\n    if err == nil || !isWeixinQRExpired(err) {\n        break\n    }\n    fmt.Println(\"QR expired, relaunching login...\") // fresh code each iteration\n}","preventionTips":["Scan immediately after the QR renders","Have WeChat open before starting the login command","In automation, gate login on operator readiness to avoid guaranteed expiries","Prefer SSH sessions that render the QR without noticeable delay"],"tags":["weixin","auth","login","qrcode","expiry"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}