fish2018/pansou · error

HTTP

Error message

HTTP %d: %s

What it means

requestJSON requires an HTTP 200 status; any other status (redirects already followed, so typically 4xx/5xx, or envelopes like 403 from a WAF) produces this error with the status code and a trimmed snippet of the response body. It wraps transport-level failures of the underlying Yingso HTTP calls before JSON decoding is attempted.

Solutions

  1. Read the status code and body snippet in the error: 429 → reduce maxKeyConcurrency or add backoff/retry; 403 → check WAF interception; 5xx → retry later.
  2. Add retry with exponential backoff around requestJSON for 429/5xx statuses (with a retry budget, e.g. 2 attempts).
  3. Reduce concurrency (maxKeyConcurrency=8, yingso.go:29) if 429s appear during bulk getKey resolution.
  4. Confirm the URL — a 404 usually means url_version from /test is stale or the path template changed; re-run fetchConfig.
  5. If a WAF challenge (HTML in body) is returned, update headers (User-Agent, Origin, Referer) to match what the official web client sends.

Example fix

// before
if resp.StatusCode != http.StatusOK {
	return fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(data)))
}
// after
if resp.StatusCode != http.StatusOK {
	if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {
		return errRetryable // let caller back off and retry
	}
	return fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(data)))
}
Defensive patterns

Strategy: retry

Validate before calling

// probe availability before real calls
resp, err := client.Get(apiBaseURL + "/test")
if err != nil || resp.StatusCode != http.StatusOK {
	log.Printf("yingso endpoint unhealthy (status=%v)", statusOrNil(resp))
}

Try / catch

results, err := p.searchImpl(client, keyword, ext)
if err != nil {
	var httpErr *HTTPStatusError
	if errors.As(err, &httpErr) && (httpErr.Status == 429 || httpErr.Status >= 500) {
		time.Sleep(exponentialBackoff(attempt))
		return p.searchImpl(client, keyword, ext)
	}
	return nil, err
}

Prevention

When it happens

Trigger: Any requestJSON call (fetchConfig GET /test, POST /search, POST /getKey) where resp.StatusCode != 200: server 5xx outages, 429 rate limiting from concurrent getKey calls, 403 from WAF/bot detection (Origin/Referer/UA checks failing), or 404 from a stale url_version path segment.

Common situations: Yingso API outage or maintenance (502/503); rate limiting under the plugin's 8-way concurrency (429); cloudflare/WAF challenge pages (403/503 with HTML body); the rotated url_version from fetchConfig produces 404 if config parsing went wrong.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07). Data as JSON: /api/errors/125fa19602389ccc. Report an issue: GitHub.

Appendix: source

Thrown at plugin/yingso/yingso.go:316

	if payload != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	data, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize+1))
	if err != nil {
		return err
	}
	if len(data) > maxResponseSize {
		return fmt.Errorf("响应超过 %d 字节", maxResponseSize)
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(data)))
	}
	if err := jsonutil.Unmarshal(data, target); err != nil {
		return fmt.Errorf("解析响应失败: %w", err)
	}
	return nil
}

func encryptPayload(payload interface{}, config apiConfig) (encryptedPayload, error) {
	no, err := randomToken(24)
	if err != nil {
		return encryptedPayload{}, err
	}
	if config.Start < 0 || config.End <= config.Start || config.End > len(no) {
		return encryptedPayload{}, fmt.Errorf("无效的加密区间 %d:%d", config.Start, config.End)
	}

	encoded, err := jsonutil.MarshalString(payload)
	if err != nil {

View on GitHub (pinned to beaa561337)