{"record":{"id":"c0158d335378baf5","repo":"sipeed/picoclaw","slug":"wecom-qr-scan-timed-out-after-s","errorCode":null,"errorMessage":"WeCom QR scan timed out after %s","messagePattern":"WeCom QR scan timed out after (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/picoclaw/internal/auth/wecom.go","lineNumber":292,"sourceCode":"\t\tAuthURL: resp.Data.AuthURL,\n\t}, nil\n}\n\nfunc pollWeComQRCodeResult(ctx context.Context, opts wecomQRFlowOptions, scode string) (wecomQRBotInfo, error) {\n\tif strings.TrimSpace(scode) == \"\" {\n\t\treturn wecomQRBotInfo{}, fmt.Errorf(\"missing WeCom QR scode\")\n\t}\n\n\ttimeoutCtx, cancel := context.WithTimeout(ctx, opts.PollTimeout)\n\tdefer cancel()\n\n\tvar scannedPrinted bool\n\n\tfor {\n\t\tstatus, err := queryWeComQRCodeStatus(timeoutCtx, opts, scode)\n\t\tif err != nil {\n\t\t\tif errors.Is(err, context.DeadlineExceeded) || errors.Is(timeoutCtx.Err(), context.DeadlineExceeded) {\n\t\t\t\treturn wecomQRBotInfo{}, fmt.Errorf(\"WeCom QR scan timed out after %s\", opts.PollTimeout)\n\t\t\t}\n\t\t\treturn wecomQRBotInfo{}, err\n\t\t}\n\n\t\tswitch strings.ToLower(status.Data.Status) {\n\t\tcase \"success\":\n\t\t\tif status.Data.BotInfo.BotID == \"\" || status.Data.BotInfo.Secret == \"\" {\n\t\t\t\treturn wecomQRBotInfo{}, fmt.Errorf(\"WeCom QR scan succeeded but bot credentials are missing\")\n\t\t\t}\n\t\t\treturn wecomQRBotInfo{\n\t\t\t\tBotID:  status.Data.BotInfo.BotID,\n\t\t\t\tSecret: status.Data.BotInfo.Secret,\n\t\t\t}, nil\n\t\tcase \"expired\":\n\t\t\treturn wecomQRBotInfo{}, fmt.Errorf(\"WeCom QR code expired, please retry\")\n\t\tcase \"scaned\", \"scanned\":\n\t\t\tif !scannedPrinted {\n\t\t\t\tfmt.Fprintln(opts.Writer, \"QR code scanned. Confirm the login in WeCom.\")","sourceCodeStart":274,"sourceCodeEnd":310,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/auth/wecom.go#L274-L310","documentation":"The WeCom QR polling loop aborted because the overall poll deadline (opts.PollTimeout, default wecomQRPollTimeout) was exceeded while queryWeComQRCodeStatus kept returning errors — specifically the error itself or the derived timeoutCtx was DeadlineExceeded. The message includes the configured duration, e.g. 'WeCom QR scan timed out after 2m0s'.","triggerScenarios":"User never scans the QR code before the poll timeout; each status query errored (network flaps) until the deadline; PollTimeout explicitly set very short via the flow options.","commonSituations":"User walked away from the terminal after starting login; phone cannot reach the QR page (region/network); flaky network making every poll fail until deadline.","solutions":["Re-run the login command and scan promptly — the QR is single-use and time-boxed","Open the printed QR Code Link URL directly on the phone if the terminal QR will not scan","Pass a larger timeout to the WeCom login flow if the default is too short for your users","Fix underlying network instability so individual polls succeed (see errors 131/137)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pass an explicit, generous timeout for interactive use\nopts := defaultWeComQRFlowOptions(5 * time.Minute)","typeGuard":"func isScanTimeout(err error) bool {\n\treturn err != nil && strings.HasPrefix(err.Error(), \"WeCom QR scan timed out\")\n}","tryCatchPattern":"info, err := pollWeComQRCodeResult(ctx, opts, session.SCode)\nif isScanTimeout(err) {\n\t// prompt user and restart the whole generate+poll flow with a fresh QR\n}","preventionTips":["Size PollTimeout to real user behavior (scan + confirm) — 2+ minutes minimum","Instruct users to keep their phone ready before starting login"],"tags":["go","wecom","timeout","polling"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}