fish2018/pansou · error
[ ] 搜索请求失败
Error message
[%s] 搜索请求失败: %w
What it means
This error is returned by MiosouPlugin.searchImpl when p.client.Do fails to execute the search HTTP request (transport-level failure). It wraps the underlying *url.Error, so timeouts, DNS failures, connection resets, and TLS errors all land here on either of the 2 attempts.
Solutions
- Inspect the wrapped *url.Error to distinguish timeout vs connection refused vs TLS
- Verify reachability: curl the apiBaseURL/search endpoint from the same host
- Increase requestTimeout (both the client Timeout and per-attempt context) if timeouts dominate
- Check DNS/proxy settings; if the host requires a proxy, configure http.Transport Proxy
Example fix
// before
resp, err := p.client.Do(req)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
// after
decision, derr := dialContext(ctx, "tcp", "proxy.local:8080")
_ = decision; _ = derr
client := &http.Client{Timeout: requestTimeout, Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}}
resp, err := client.Do(req)
if err != nil {
var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
return nil, fmt.Errorf("[%s] 搜索超时(%v): %w", p.Name(), requestTimeout, err)
}
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
} Defensive patterns
Strategy: retry
Validate before calling
// Go: pre-flight reachability check
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, http.MethodHead, apiBaseURL, nil)
if _, err := p.client.Do(req); err != nil {
// upstream unreachable: fail fast with clear message
} Type guard
func isTimeoutErr(err error) bool {
var netErr net.Error
return errors.As(err, &netErr) && netErr.Timeout()
} Try / catch
resp, err := p.client.Do(req)
if err != nil {
var netErr net.Error
switch {
case errors.As(err, &netErr) && netErr.Timeout():
return nil, fmt.Errorf("search timed out after %v: %w", requestTimeout, err)
default:
return nil, fmt.Errorf("search transport error: %w", err)
}
} Prevention
- Set http.Client Timeout and context timeouts consistently
- Configure ProxyFromEnvironment if the host needs a proxy
- Check DNS health on the deployment host
- Distinguish timeouts from refusals in logs
When it happens
Trigger: searchImpl calls p.client.Do(req) and it returns a non-nil error: requestTimeout context expired, DNS resolution of the API host failed, TCP connect refused/reset, TLS handshake failure, or the client's Timeout fired first.
Common situations: The miosou API host is down or blocked from the user's network region; requestTimeout is too short for a slow link; a corporate proxy intercepts TLS; DNS misconfiguration on the host.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/515d5097f7cad07c.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/miosou/miosou.go:104
if keyword == "" {
return nil, nil
}
if err := p.ensureGate(); err != nil {
return nil, err
}
for attempt := 0; attempt < 2; attempt++ {
ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiBaseURL+"/search?keyword="+url.QueryEscape(keyword), nil)
if err != nil {
cancel()
return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
}
setHeaders(req, "text/event-stream")
resp, err := p.client.Do(req)
if err != nil {
cancel()
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
if isAnubisGateResponse(resp) {
resp.Body.Close()
cancel()
p.invalidateGate()
if err := p.ensureGate(); err != nil {
return nil, err
}
continue
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
cancel()
return nil, fmt.Errorf("[%s] 搜索接口返回状态码: %d", p.Name(), resp.StatusCode)
}
groups, err := parseSearchStream(resp.Body)
resp.Body.Close()
if err != nil {View on GitHub (pinned to beaa561337)