sipeed/picoclaw · error

failed to query WeCom QR result: %w

Error message

failed to query WeCom QR result: %w

What it means

A single poll iteration failed at the HTTP layer: doWeComJSONGet returned an error while GETting the query URL. This wraps transport failures, non-200 statuses ('unexpected status 502 Bad Gateway...'), and body decode failures ('decode JSON response...'). Unlike DeadlineExceeded, any other error here aborts the whole login immediately — one transient 502 kills the flow.

Source

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

		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):
		}
	}
}

func queryWeComQRCodeStatus(ctx context.Context, opts wecomQRFlowOptions, scode string) (wecomQRQueryResponse, error) {
	queryURL, err := buildWeComQRQueryURL(opts.QueryURL, scode)
	if err != nil {
		return wecomQRQueryResponse{}, err
	}

	var resp wecomQRQueryResponse
	if err := doWeComJSONGet(ctx, opts.HTTPClient, queryURL, &resp); err != nil {
		return wecomQRQueryResponse{}, fmt.Errorf("failed to query WeCom QR result: %w", err)
	}
	if resp.ErrCode != 0 {
		return wecomQRQueryResponse{}, fmt.Errorf(
			"failed to query WeCom QR result: errcode=%d errmsg=%s",
			resp.ErrCode,
			resp.ErrMsg,
		)
	}

	return resp, nil
}

func buildWeComQRGenerateURL(baseURL, sourceID string, platformCode int) (string, error) {
	u, err := url.Parse(baseURL)
	if err != nil {
		return "", fmt.Errorf("invalid WeCom QR generate URL: %w", err)
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Check whether the failure is persistent (curl the query endpoint) or was a one-off, then simply retry the login
  2. Stabilize network/VPN before starting an interactive login
  3. Update picoclaw — newer builds may tolerate transient poll errors by continuing the loop
  4. If a proxy intercepts, bypass it for the WeCom relay domain

Example fix

// before
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
}

// after: tolerate isolated poll failures until the overall deadline
status, err := queryWeComQRCodeStatus(timeoutCtx, opts, scode)
if err != nil {
	if errors.Is(timeoutCtx.Err(), context.DeadlineExceeded) {
		return wecomQRBotInfo{}, fmt.Errorf("WeCom QR scan timed out after %s", opts.PollTimeout)
	}
	if !errors.Is(err, context.Canceled) {
		select {
		case <-timeoutCtx.Done():
			return wecomQRBotInfo{}, fmt.Errorf("WeCom QR scan timed out after %s", opts.PollTimeout)
		case <-time.After(opts.PollInterval):
			continue
		}
	}
	return wecomQRBotInfo{}, err
}
Defensive patterns

Strategy: retry

Type guard

func isTransientPollErr(err error) bool {
	var netErr net.Error
	if errors.As(err, &netErr) { return true }
	return strings.Contains(err.Error(), "unexpected status 50") ||
		strings.Contains(err.Error(), "unexpected status 429")
}

Try / catch

status, err := queryWeComQRCodeStatus(timeoutCtx, opts, scode)
if err != nil && isTransientPollErr(err) && timeoutCtx.Err() == nil {
	select {
	case <-timeoutCtx.Done():
		return wecomQRBotInfo{}, fmt.Errorf("WeCom QR scan timed out after %s", opts.PollTimeout)
	case <-time.After(opts.PollInterval):
		continue
	}
}

Prevention

When it happens

Trigger: A single 502/503 from a load balancer between polls; TLS reset mid-request; response body was HTML (proxy error page) so JSON decode failed; HTTP timeout on one poll exceeding wecomQRHTTPTimeout.

Common situations: Relay behind a flaky CDN; momentary network switch (Wi-Fi to VPN); captive portal intercepting mid-flow; relay rolling deploy causing one 502.

Related errors


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