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

  1. Log resp.Status (not just the code) and, ideally, the response body to see whether it's 403 WAF, 429 rate-limit, or 5xx
  2. For 429: throttle or serialize the per-page goroutines and add backoff
  3. For 403: update headers/cookies/UA to match what the site currently expects
  4. For 404: update the MelostSearchAPI constant after a site migration
  5. 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

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)