fish2018/pansou · error
[ ] 搜索请求失败
Error message
[%s] 搜索请求失败: %w
What it means
The NSGame plugin wraps any transport-level failure of its GET search request to nsthwj.cn (issued inside doRequestWithRetry) with this message, preserving the underlying error via %w. It fires before any status-code or body inspection, so it always means the HTTP round trip itself failed after the plugin's internal retries were exhausted. The plugin name is prefixed to help identify which aggregator plugin failed.
Solutions
- Verify basic connectivity to nsthwj.cn (curl -v the apiURL) from the host running the app
- Check the wrapped cause with errors.Is/errors.As for context.DeadlineExceeded vs DNS vs TLS errors and fix the specific layer (DNS, proxy, timeout)
- Increase the timeout or retry count if the site is slow rather than unreachable
- Configure HTTP_PROXY/HTTPS_PROXY if the host requires egress via a proxy
- If the site is permanently unreachable, disable the NSGame plugin
Example fix
// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
return nil, fmt.Errorf("[%s] 搜索请求超时(%v): %w", p.Name(), defaultTimeout, err)
}
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
} Defensive patterns
Strategy: retry
Validate before calling
func canReachNSGame() error {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://nsthwj.cn/", nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
resp.Body.Close()
return nil
} Try / catch
results, err := plugin.Search(keyword, ext)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
// timeout: schedule retry with backoff
} else {
log.Warn("nsgame unreachable, skipping plugin", "err", err)
}
return fallbackResults
} Prevention
- Probe the upstream host with a cheap health check before enabling the plugin
- Always inspect the wrapped error with errors.Is/As instead of treating all failures the same
- Set realistic timeouts and retry counts for slow upstreams
- Run from a network that can reach the target region
When it happens
Trigger: doRequestWithRetry returns an error: DNS resolution failure for the API host, TCP connect failure, TLS handshake error, context deadline exceeded (defaultTimeout), client.Do transport error, or all retries exhausted on retryable errors.
Common situations: Target site is down or blocked in the user's region/network (common with nsthwj.cn), corporate proxy or firewall blocking the request, no internet connectivity, DNS poisoning, or the site added anti-bot measures that reset connections.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/404d4384e07e4383.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/nsgame/nsgame.go:162
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
// 3. 创建请求
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
// 4. 设置请求头
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
p.setRequestHeaders(req, "https://nsthwj.cn/")
// 5. 发送请求(带重试机制)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("[%s] 请求返回状态码: %d", p.Name(), resp.StatusCode)
}
// 6. 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取响应失败: %w", p.Name(), err)
}
// 7. 解析JSON响应
var apiResp NSGameResponse
if err := json.Unmarshal(body, &apiResp); err != nil {
return nil, fmt.Errorf("[%s] JSON解析失败: %w", p.Name(), err)
}View on GitHub (pinned to beaa561337)