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

  1. Map the status: 403 → blocked, change headers/cookies/IP; 429 → back off and slow down; 5xx → retry later
  2. Verify setDuanjuwHeaders produces browser-like Referer and User-Agent
  3. Reduce search frequency or add caching for repeated keywords
  4. 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

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)