fish2018/pansou · error
unexpected status code
Error message
unexpected status code: %d
What it means
After client.Do succeeds, searchPage requires HTTP 200; any other status returns "unexpected status code: %d". This means melost.cn responded but rejected the search request — the plugin treats anything non-200 as a failed page search.
Solutions
- Log resp.Status (not just the code) and, ideally, the response body to see whether it's 403 WAF, 429 rate-limit, or 5xx
- For 429: throttle or serialize the per-page goroutines and add backoff
- For 403: update headers/cookies/UA to match what the site currently expects
- For 404: update the MelostSearchAPI constant after a site migration
- Remember doSearch tolerates partial failures — this only fails the whole search when all pages fail
Example fix
// before
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
// after
if resp.StatusCode != http.StatusOK {
b, _ := io.ReadAll(io.LimitReader(resp.Body, 256))
return nil, fmt.Errorf("unexpected status code: %d (%s) body=%q", resp.StatusCode, resp.Status, b)
} Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: confirm endpoint still exists
resp, err := client.Head(MelostSearchAPI)
if err == nil && (resp.StatusCode == 404 || resp.StatusCode == 410) {
log.Printf("melost endpoint moved (status %d)", resp.StatusCode)
} Try / catch
if resp.StatusCode != http.StatusOK {
switch {
case resp.StatusCode == 429:
// back off and retry the page
case resp.StatusCode == 403:
// refresh cookies/headers
default:
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
} Prevention
- Limit concurrency of page requests to avoid 429s
- Keep UA/headers in sync with the site's current expectations
- Alert on 404/410 to catch endpoint migrations early
- Log resp.Status and body snippet for every non-200
When it happens
Trigger: The melost.cn search API returns 403 (anti-bot/WAF block), 429 (rate limited by the concurrent per-page requests), 404 (endpoint moved), or 5xx (server error) for a searchPage POST.
Common situations: Firing DefaultMaxPages requests simultaneously trips rate limiting; site anti-scraping protections reject the plugin's fixed headers/UA; melost.cn changed its API path or is temporarily down.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/8c6af16bb7c23164.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/melost/melost.go:177
if err != nil {
return nil, fmt.Errorf("create request failed: %w", err)
}
req.Header.Set("Content-Type", "application/json")
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("Origin", "https://www.melost.cn")
req.Header.Set("Referer", DefaultReferer)
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response failed: %w", err)
}
var apiResp MelostResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
return nil, fmt.Errorf("decode response failed: %w", err)
}
if apiResp.Code != 200 {
return nil, fmt.Errorf("api returned error: %s", apiResp.Msg)
}
return apiResp.Data.List, nil
}View on GitHub (pinned to beaa561337)