sipeed/picoclaw · warning

WeCom QR code expired, please retry

Error message

WeCom QR code expired, please retry

What it means

The QR session status polled as 'expired': the WeCom relay invalidated the QR code because it was not scanned-and-confirmed within its validity window. The session's scode is dead; polling cannot succeed and the flow aborts with a retry instruction.

Source

Thrown at cmd/picoclaw/internal/auth/wecom.go:307

		status, err := queryWeComQRCodeStatus(timeoutCtx, opts, scode)
		if err != nil {
			if errors.Is(err, context.DeadlineExceeded) || errors.Is(timeoutCtx.Err(), context.DeadlineExceeded) {
				return wecomQRBotInfo{}, fmt.Errorf("WeCom QR scan timed out after %s", opts.PollTimeout)
			}
			return wecomQRBotInfo{}, err
		}

		switch strings.ToLower(status.Data.Status) {
		case "success":
			if status.Data.BotInfo.BotID == "" || status.Data.BotInfo.Secret == "" {
				return wecomQRBotInfo{}, fmt.Errorf("WeCom QR scan succeeded but bot credentials are missing")
			}
			return wecomQRBotInfo{
				BotID:  status.Data.BotInfo.BotID,
				Secret: status.Data.BotInfo.Secret,
			}, nil
		case "expired":
			return wecomQRBotInfo{}, fmt.Errorf("WeCom QR code expired, please retry")
		case "scaned", "scanned":
			if !scannedPrinted {
				fmt.Fprintln(opts.Writer, "QR code scanned. Confirm the login in WeCom.")
				scannedPrinted = true
			}
		}

		select {
		case <-timeoutCtx.Done():
			if errors.Is(timeoutCtx.Err(), context.DeadlineExceeded) {
				return wecomQRBotInfo{}, fmt.Errorf("WeCom QR scan timed out after %s", opts.PollTimeout)
			}
			return wecomQRBotInfo{}, timeoutCtx.Err()
		case <-time.After(opts.PollInterval):
		}
	}
}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Re-run the WeCom login command to get a fresh QR code and scan immediately
  2. Avoid starting multiple simultaneous login sessions (a new code can invalidate the old one)
  3. Use the printed QR Code Link URL directly if camera scanning is slow
  4. Confirm the login promptly in the WeCom app after scanning
Defensive patterns

Strategy: retry

Type guard

func isQRExpired(err error) bool {
	return err != nil && strings.Contains(err.Error(), "QR code expired")
}

Try / catch

info, err := pollWeComQRCodeResult(ctx, opts, session.SCode)
if isQRExpired(err) {
	// automatically fetch a fresh QR and restart polling (bounded retries)
}

Prevention

When it happens

Trigger: User requested a QR code but scanned it too late; user scanned but never confirmed in the WeCom app within the validity window; relay expired the code server-side due to a newer code being issued for the same source.

Common situations: User starts login, gets distracted, then scans minutes later; generating a QR in a second terminal invalidates the first; slow phone camera making the user miss the window.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/3ffaf1c6f5acc2f9. Report an issue: GitHub.