fish2018/pansou · error
期望302重定向,但得到状态码
Error message
期望302重定向,但得到状态码: %d
What it means
ClxiongPlugin.getSearchID expected the search POST to respond with an HTTP 301/302 redirect (the searchid is extracted from the Location header) but the server returned some other status. This breaks the plugin's core assumption about the site's two-step search flow. Common offending codes: 200 (site changed flow or serves an HTML challenge), 403 (blocked), 429 (rate limited), 5xx (server error).
Solutions
- Log the response body alongside the status code to see whether it's a CAPTCHA/challenge, rate-limit notice, or a changed flow.
- If 403: update User-Agent/cookies/headers in getSearchID to mimic a browser.
- If 429: implement backoff and reduce search frequency; honor Retry-After.
- If 200 with HTML: the site changed its search flow — update getSearchID to extract the searchid from the new response (page HTML or a JSON API) instead of expecting a redirect.
- If 5xx: retry later; consider adding 5xx handling to the retry loop.
Example fix
// before
if resp.StatusCode != 302 && resp.StatusCode != 301 {
return "", fmt.Errorf("期望302重定向,但得到状态码: %d", resp.StatusCode)
}
// after
if resp.StatusCode != 302 && resp.StatusCode != 301 {
snippet := ""
if b, e := io.ReadAll(io.LimitReader(resp.Body, 512)); e == nil { snippet = string(b) }
return "", fmt.Errorf("期望302重定向,但得到状态码: %d, body: %q", resp.StatusCode, snippet)
} Defensive patterns
Strategy: fallback
Validate before calling
// pre-flight: confirm the endpoint still redirects
func stillRedirects(client *http.Client, postURL string, form url.Values) bool {
client.CheckRedirect = func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }
resp, err := client.PostForm(postURL, form)
if err != nil { return false }
defer resp.Body.Close()
return resp.StatusCode == 301 || resp.StatusCode == 302
} Try / catch
searchID, err := p.getSearchID(keyword)
if err != nil {
if strings.Contains(err.Error(), "期望302重定向") {
// site no longer redirects: switch to alternate extraction path
return p.searchViaHTMLFallback(keyword)
}
return nil, err
} Prevention
- Configure CheckRedirect to NOT follow redirects so the Location header is observable.
- Log response body snippets on unexpected statuses to distinguish CAPTCHA vs changed flow.
- Refresh browser-like headers/cookies to avoid 403 blocks.
- Add handling for 429/5xx in the retry loop instead of failing fast.
- Pin an integration test that asserts the endpoint still returns 301/302.
When it happens
Trigger: getSearchID's POST (after its retry loop succeeded in getting any response) receives resp.StatusCode not in {301, 302}; the function immediately returns "期望302重定向,但得到状态码: %d" to SearchWithResult, failing the whole search.
Common situations: clxiong deployed an anti-bot page or JS challenge answering 200 instead of redirecting; the client is rate limited (429) after rapid searches; blocked User-Agent yields 403; the site's search endpoint changed and now returns 404 or 200 with different semantics; server-side errors (500/503).
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/f10edf7cdd6dee73.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/clxiong/clxiong.go:157
if lastErr == nil && (resp.StatusCode == 302 || resp.StatusCode == 301) {
break
}
if resp != nil {
resp.Body.Close()
}
if i < MaxRetries-1 {
time.Sleep(RetryDelay)
}
}
if lastErr != nil {
return "", lastErr
}
defer resp.Body.Close()
// 检查重定向响应
if resp.StatusCode != 302 && resp.StatusCode != 301 {
return "", fmt.Errorf("期望302重定向,但得到状态码: %d", resp.StatusCode)
}
// 从Location头部提取searchid
location := resp.Header.Get("Location")
if location == "" {
return "", fmt.Errorf("重定向响应中没有Location头部")
}
// 解析searchid
searchID := p.extractSearchIDFromLocation(location)
if searchID == "" {
return "", fmt.Errorf("无法从Location中提取searchid: %s", location)
}
if p.debugMode {
log.Printf("[CLXIONG] 获取到searchid: %s", searchID)
}
View on GitHub (pinned to beaa561337)