fish2018/pansou · error

HTTP 状态码

Error message

HTTP 状态码 %d

What it means

doRequestWithRetry records a non-nil error from the HTTP attempt; when err == nil but the loop still ended (i.e., the response had a non-acceptable status), it synthesizes "HTTP 状态码 %d" from resp.StatusCode. This becomes the wrapped cause of the final "重试 N 次后仍然失败" error. Note the condition looks inverted at a glance but is correct: it fires only when the attempt returned a response (err == nil) rather than a transport error.

Solutions

  1. Log each attempt's status code inside the retry loop to see the actual codes received.
  2. If 403/429, slow down request rate, rotate headers/cookies, or extend backoff between retries.
  3. If 404, verify the endpoint URL is still valid after a site update.
  4. Consider treating 4xx as non-retryable to fail fast instead of burning all retries.

Example fix

// before
lastErr = err
if err == nil {
    lastErr = fmt.Errorf("HTTP 状态码 %d", resp.StatusCode)
}
// after
if err != nil {
    lastErr = err
} else {
    lastErr = fmt.Errorf("HTTP 状态码 %d", resp.StatusCode)
}
Defensive patterns

Strategy: fallback

Try / catch

resp, err := p.doRequestWithRetry(req, client)
if err != nil {
    if strings.Contains(err.Error(), "HTTP 状态码") {
        // server kept returning a bad status; do not retry immediately
        return nil, fmt.Errorf("endpoint unhealthy: %w", err)
    }
    return nil, err
}

Prevention

When it happens

Trigger: All retry attempts in doRequestWithRetry return responses whose status codes are not accepted by the plugin's success check (e.g. non-200), so the last such status is stored as the sentinel error.

Common situations: Target site persistently returning 403 (anti-bot/WAF), 429 (rate limit) or 5xx across all retries; endpoint moved returning 404; required cookies/headers missing.

Related errors


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

Appendix: source

Thrown at plugin/cyg/cyg.go:395

			// 指数退避重试
			backoff := time.Duration(1<<uint(i-1)) * 200 * time.Millisecond
			time.Sleep(backoff)
		}

		// 克隆请求避免并发问题
		reqClone := req.Clone(req.Context())

		resp, err := client.Do(reqClone)
		if err == nil && resp.StatusCode == 200 {
			return resp, nil
		}

		if resp != nil {
			resp.Body.Close()
		}
		lastErr = err
		if err == nil {
			lastErr = fmt.Errorf("HTTP 状态码 %d", resp.StatusCode)
		}
	}

	return nil, fmt.Errorf("重试 %d 次后仍然失败: %w", maxRetries, lastErr)
}

// parseExtOptions 从ext参数中解析搜索选项
func (p *CygPlugin) parseExtOptions(ext map[string]interface{}) CygSearchOptions {
	opts := CygSearchOptions{
		PerPage: 20,
		Page:    1,
		OrderBy: "date",
		Order:   "desc",
	}

	if ext == nil {
		return opts
	}

View on GitHub (pinned to beaa561337)