fish2018/pansou · error
unexpected HTTP status
Error message
unexpected HTTP status: %s
What it means
doRequestWithRetry in the Meitizy plugin only accepts HTTP 200 responses; any other status (e.g. 403, 429, 5xx) is recorded as "unexpected HTTP status: %s" using resp.Status. It is a retry wrapper around client.Do called by searchImpl and fetchDetailLinks; a non-200 on each attempt means the upstream meitizy endpoint rejected the request or is failing.
Solutions
- Check the full wrapped status string (e.g. 429 Too Many Requests) to identify the class of problem; for 429 back off and retry later
- Inspect and refresh any required cookies/anti-bot tokens or headers for meitizy; 403 usually means WAF blocking
- Verify the upstream endpoint URL is still valid (404/301 suggest the site changed)
- If 5xx, treat as upstream outage and retry after a delay; check site availability manually
Example fix
// before: only 200 accepted, error loses status code detail beyond resp.Status
if err == nil && resp.StatusCode == 200 {
return resp, nil
}
// after: log body snippet for non-200 to aid diagnosis
if err == nil && resp.StatusCode != 200 {
b, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
lastErr = fmt.Errorf("unexpected HTTP status: %s body=%q", resp.Status, b)
} Defensive patterns
Strategy: retry
Validate before calling
if resp.StatusCode == http.StatusTooManyRequests {
if ra, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
time.Sleep(time.Duration(ra) * time.Second)
}
} Try / catch
resp, err := doRequestWithRetry(req, client)
if err != nil {
var statusErr *fmt.Errorf
if errors.As(err, &statusErr) && strings.Contains(err.Error(), "unexpected HTTP status") {
// handle non-200: back off, refresh credentials, or degrade
}
return nil, err
} Prevention
- Log resp.Status and a body snippet on every non-200 to distinguish 403/429/5xx
- Refresh anti-bot cookies/headers before they expire
- Throttle request rate to stay under the site's limits
- Alert on repeated retry-exhaustion for the same endpoint
When it happens
Trigger: Any call to p.searchImpl or p.fetchDetailLinks where the meitizy server answers with a status other than 200 on all retry attempts: rate limiting (429), WAF/anti-bot blocking (403), gateway errors (502/503), or a moved endpoint returning 404.
Common situations: The site's anti-scraping protection blocks the plugin's requests (missing/invalid cookies or fingerprint), the target URL changed, heavy keyword use triggers rate limiting, or the upstream service is temporarily down.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/0415ac6c949465c5.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/meitizy/meitizy.go:393
if req.Body != nil {
// 读取原始body
bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
lastErr = err
continue
}
req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
reqClone.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
resp, err := client.Do(reqClone)
if err == nil && resp.StatusCode == 200 {
return resp, nil
}
if resp != nil {
if err == nil {
lastErr = fmt.Errorf("unexpected HTTP status: %s", resp.Status)
}
resp.Body.Close()
}
if err != nil {
lastErr = err
}
}
return nil, fmt.Errorf("[%s] 重试 %d 次后仍然失败: %w", p.Name(), maxRetries, lastErr)
}
View on GitHub (pinned to beaa561337)