sipeed/picoclaw · warning
login timeout
Error message
login timeout
What it means
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).
Source
Thrown at pkg/channels/weixin/auth.go:78
HalfBlocks: true,
}
qrterminal.GenerateWithConfig(qrResp.QrcodeImgContent, qrconfig)
fmt.Printf("\nQR Code Link: %s\n\n", qrResp.QrcodeImgContent)
fmt.Println("Waiting for scan...")
timeoutCtx, cancel := context.WithTimeout(ctx, opts.Timeout)
defer cancel()
pollTicker := time.NewTicker(2 * time.Second)
defer pollTicker.Stop()
scannedPrinted := false
for {
select {
case <-timeoutCtx.Done():
return "", "", "", "", fmt.Errorf("login timeout")
case <-pollTicker.C:
statusResp, err := pollAPI.GetQRCodeStatus(timeoutCtx, qrResp.Qrcode)
if err != nil {
// Long poll timeout or temporary error
continue
}
switch statusResp.Status {
case "wait":
// still waiting
case "scaned":
if !scannedPrinted {
fmt.Println("👀 QR Code scanned! Please confirm login on your WeChat app...")
scannedPrinted = true
}
case "confirmed":
if statusResp.BotToken == "" || statusResp.IlinkBotID == "" {
return "", "", "", "", fmt.Errorf("login confirmed but missing bot_token or ilink_bot_id")View on GitHub (pinned to 49183d7e8d)
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
Example fix
// before
opts := weixin.LoginOpts{Timeout: 5 * time.Minute}
// after
opts := weixin.LoginOpts{Timeout: 10 * time.Minute} Defensive patterns
Strategy: retry
Validate before calling
// assert a sane timeout and an operator before starting
if opts.Timeout < 2*time.Minute {
opts.Timeout = 5 * time.Minute
}
if !operatorReadyToScan() {
return errors.New("no operator present; aborting login")
} Type guard
func isWeixinLoginTimeout(err error) bool {
return err != nil && strings.Contains(err.Error(), "login timeout")
} Try / catch
if _, _, _, _, err := weixin.Login(ctx, opts); err != nil {
if isWeixinLoginTimeout(err) {
// benign: re-run with a fresh QR; consider a longer Timeout
opts.Timeout *= 2
_, _, _, _, err = weixin.Login(ctx, opts)
}
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- failed to get qrcode: %w
- qrcode expired, please try again
- failed to create api client: %w
- login confirmed but missing bot_token or ilink_bot_id
- failed to load config: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/63e5cdf2d1843026.
Report an issue: GitHub.