fish2018/pansou · error
[ ] 搜索 API 请求失败
Error message
[%s] 搜索 API 请求失败: %w
What it means
searchImpl sends the search API request through doRequestWithRetry; when even the retrying client fails, the plugin falls back to HTML search via searchWeb, attaching this wrapped error as the apiErr context. It means the JSON API endpoint was unreachable or errored.
Solutions
- Inspect the wrapped cause from doRequestWithRetry
- Confirm the API host resolves and is reachable (curl the searchURL)
- Verify DefaultTimeout is generous enough for the upstream
- Rely on/verify the searchWeb HTML fallback results
Example fix
// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return p.searchWeb(client, keyword, fmt.Errorf("[%s] 搜索 API 请求失败: %w", p.Name(), err))
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
log.Printf("feikuai API request failed, falling back to web: %v", err)
return p.searchWeb(client, keyword, fmt.Errorf("[%s] 搜索 API 请求失败: %w", p.Name(), err))
} Defensive patterns
Strategy: fallback
Validate before calling
// probe API endpoint before relying on it resp, err := client.Head(apiBaseURL) _ = err // if err != nil, expect searchWeb fallback path
Try / catch
results, err := plugin.Search(keyword)
if err != nil && strings.Contains(err.Error(), "搜索 API 请求失败") {
log.Printf("feikuai API down, web fallback also attempted: %v", err)
// surface partial info; the error already embeds the API cause
} Prevention
- Treat API-down as normal and always keep the HTML fallback healthy
- Monitor the fallback path's success rate separately
- Rotate domain constants automatically when they stop resolving
- Set realistic timeouts so transient slowness doesn't exhaust retries
When it happens
Trigger: doRequestWithRetry returns non-nil (transport error, timeout, exhausted retries) for the API GET request.
Common situations: API domain blocked or DNS-poisoned, TLS errors after a site domain change, timeouts under DefaultTimeout, upstream API temporarily down.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/bf4ecf9a702709f7.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/feikuai/feikuai.go:127
defer cancel()
// 创建请求
req, err := http.NewRequestWithContext(ctx, "GET", searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建请求失败: %w", p.Name(), err)
}
// 设置请求头
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 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")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Referer", "https://feikuai.tv/")
// 发送请求(带重试)
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
return p.searchWeb(client, keyword, fmt.Errorf("[%s] 搜索 API 请求失败: %w", p.Name(), err))
}
defer resp.Body.Close()
// 检查状态码
if resp.StatusCode != 200 {
return p.searchWeb(client, keyword, fmt.Errorf("[%s] 搜索 API 返回状态码: %d", p.Name(), resp.StatusCode))
}
// 读取并解析JSON响应
body, err := io.ReadAll(resp.Body)
if err != nil {
return p.searchWeb(client, keyword, fmt.Errorf("[%s] 读取 API 响应失败: %w", p.Name(), err))
}
var apiResp FeikuaiAPIResponse
if err := json.Unmarshal(body, &apiResp); err != nil {
return p.searchWeb(client, keyword, fmt.Errorf("[%s] API JSON 解析失败: %w", p.Name(), err))
}View on GitHub (pinned to beaa561337)