fish2018/pansou · error
[ ] 搜索返回状态码
Error message
[%s] 搜索返回状态码: %d
What it means
The duanjuw plugin returns this when the search response status code is not 200. Any non-OK status (403 WAF block, 429 rate limit, 5xx) is treated as a failed search since the plugin expects an HTML results page.
Solutions
- Map the status: 403 → blocked, change headers/cookies/IP; 429 → back off and slow down; 5xx → retry later
- Verify setDuanjuwHeaders produces browser-like Referer and User-Agent
- Reduce search frequency or add caching for repeated keywords
- Re-check the site manually; if it now requires JS challenge, the plugin needs updating
Example fix
// before
if err != nil { return err }
// after
var statusErr interface{ Error() string }
if strings.Contains(err.Error(), "搜索返回状态码: 429") {
time.Sleep(time.Minute)
items, err = p.searchImpl(k)
} Defensive patterns
Strategy: fallback
Validate before calling
// probe status before searches
resp, err := client.Get(duanjuwBaseURL + "/")
if err == nil {
if resp.StatusCode == 403 || resp.StatusCode == 429 { log.Println("currently blocked/rate-limited") }
resp.Body.Close()
} Try / catch
if err != nil && strings.Contains(err.Error(), "搜索返回状态码: 429") {
select {
case <-time.After(time.Minute):
return p.searchImpl(k)
case <-ctx.Done():
return ctx.Err()
}
} Prevention
- Throttle search frequency; cache repeated keywords
- Rotate User-Agent/cookies when 403s appear
- Differentiate backoff by status code family
- Watch for JS-challenge redirects indicating WAF changes
When it happens
Trigger: doDuanjuwRequestWithRetry succeeded at transport level but resp.StatusCode != http.StatusOK in searchImpl.
Common situations: Anti-bot protection blocking datacenter IPs; too-frequent searches hitting a rate limit; site returning 5xx during incidents; redirect to a login or verification page.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/6645be1e3e5f42fc.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/duanjuw/duanjuw.go:121
searchURL := fmt.Sprintf(duanjuwSearchURL, url.QueryEscape(keyword))
ctx, cancel := context.WithTimeout(context.Background(), duanjuwSearchTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
if err != nil {
return nil, fmt.Errorf("[%s] 创建搜索请求失败: %w", p.Name(), err)
}
setDuanjuwHeaders(req, duanjuwBaseURL+"/")
resp, err := doDuanjuwRequestWithRetry(req, client)
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] 搜索返回状态码: %d", p.Name(), resp.StatusCode)
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 解析搜索结果失败: %w", p.Name(), err)
}
items := p.parseSearchResults(doc)
if len(items) == 0 {
return []model.SearchResult{}, nil
}
// The current site renders search results as numbered chat entries with
// direct pan links. Older pages still use result cards and need detail fetches.
for _, item := range items {
if len(item.Links) > 0 {
return plugin.FilterResultsByKeyword(items, keyword), nil
}
}View on GitHub (pinned to beaa561337)