fish2018/pansou · error

[ ] 搜索请求失败

Error message

[%s] 搜索请求失败: %w

What it means

fetchSearch wraps any error from client.Do — the actual HTTP round trip for the search POST — as '[%s] 搜索请求失败'. client.Do returns an error for DNS resolution failure, TCP connect failure, TLS errors, and, critically, when the request's context deadline (requestTimeout) expires before the response completes. All transport-level problems surface here rather than as non-200 statuses.

Solutions

  1. Read the wrapped %w error: 'context deadline exceeded' means requestTimeout is too short or the site is slow — consider increasing requestTimeout.
  2. Verify DNS/connectivity to the site with curl from the same host (curl -v '<baseURL>') to rule out network/DNS/firewall issues.
  3. If a proxy is required on this machine, set HTTP(S)_PROXY or configure the http.Client's Transport with a ProxyFunc.
  4. Check for a redirect to a new domain (site moved) and update baseURL; add a retry with backoff for transient connection resets.

Example fix

// before
resp, err := client.Do(req)
if err != nil {
    return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
// after
resp, err := client.Do(req)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        return nil, fmt.Errorf("[%s] 搜索请求超时(>%s), 可尝试增大 requestTimeout: %w", p.Name(), requestTimeout, err)
    }
    return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
Defensive patterns

Strategy: retry

Validate before calling

// 网络连通性预检(可选)
if _, err := net.LookupHost("5266ys-site-domain"); err != nil {
    return nil, fmt.Errorf("DNS解析失败: %w", err)
}

Try / catch

result, err := p.Search(ctx, keyword)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        // 增大超时后重试
    } else if isTransientNetErr(err) {
        // 指数退避重试 2-3 次
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: client.Do(req) returns err inside fetchSearch: DNS failure for the site domain, connection refused/reset/timeout, TLS handshake failure, context deadline exceeded from context.WithTimeout(requestTimeout), or a proxy misconfiguration in the default transport.

Common situations: The target site is blocked or geo-restricted on the host machine; a corporate firewall/proxy drops the POST; the site is slow and requestTimeout (a few seconds) is too short; the site domain changed and DNS no longer resolves.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/dcb8d69e4de05412. Report an issue: GitHub.

Appendix: source

Thrown at plugin/5266ys/5266ys.go:182

}

func (p *Plugin) fetchSearch(client *http.Client, keyword string) (*goquery.Document, error) {
	encoded, err := encodeGB18030(keyword)
	if err != nil {
		return nil, fmt.Errorf("[%s] 编码搜索关键词失败: %w", p.Name(), err)
	}
	form := "show=title%2Csmalltext&tempid=1&tbname=article&keyboard=" + url.QueryEscape(string(encoded)) + "&submit="
	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	defer cancel()
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+searchPath, strings.NewReader(form))
	if err != nil {
		return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
	}
	setHeaders(req, baseURL+"/")
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("[%s] 搜索请求返回 HTTP %d", p.Name(), resp.StatusCode)
	}
	body, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))
	if err != nil {
		return nil, fmt.Errorf("[%s] 读取搜索结果失败: %w", p.Name(), err)
	}
	decoded, err := decodeGB18030(body)
	if err != nil {
		return nil, fmt.Errorf("[%s] 解码搜索结果失败: %w", p.Name(), err)
	}
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(decoded))
	if err != nil {
		return nil, fmt.Errorf("[%s] 解析搜索结果失败: %w", p.Name(), err)
	}
	return doc, nil

View on GitHub (pinned to beaa561337)