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
- Read the status code and body snippet in the error: 429 → reduce maxKeyConcurrency or add backoff/retry; 403 → check WAF interception; 5xx → retry later.
- Add retry with exponential backoff around requestJSON for 429/5xx statuses (with a retry budget, e.g. 2 attempts).
- Reduce concurrency (maxKeyConcurrency=8, yingso.go:29) if 429s appear during bulk getKey resolution.
- Confirm the URL — a 404 usually means url_version from /test is stale or the path template changed; re-run fetchConfig.
- 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
- Retry 429/5xx with exponential backoff and a small retry budget.
- Reduce maxKeyConcurrency (8) if rate-limit 429s occur during bulk resolution.
- Keep request headers (User-Agent, Origin, Referer) aligned with the official web client to pass WAF checks.
- Re-run fetchConfig when 404s appear — a stale url_version breaks endpoint paths.
- Alert on sustained 5xx so API outages are distinguished from local misconfig.
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)