fish2018/pansou · error

[susu] 按钮列表请求返回状态码

Error message

[susu] 按钮列表请求返回状态码: %d

What it means

getLinks throws this when the button-list API responds with a non-200 status code after a successful transport round-trip. The body is not read or parsed; the status code is reported verbatim so callers can diagnose whether it's auth (403), rate limiting (429), server error (5xx), or a wrong-endpoint 404.

Solutions

  1. Read the status code from the message; 403/503 usually means bot protection — refresh setAPIHeaders/cookies.
  2. If 429, add delays between getLinks calls or implement backoff.
  3. If 404/405, verify ButtonListURL against the current site API.
  4. If 5xx, retry later or extend doRequestWithRetry to retry on 5xx.
  5. Capture a sample response body (before discarding) to confirm WAF/challenge pages.

Example fix

null
Defensive patterns

Strategy: retry

Try / catch

links, err := p.getLinks(postID)
if err != nil {
    if strings.Contains(err.Error(), "按钮列表请求返回状态码: 429") {
        time.Sleep(backoff)
        // retry once
    }
}

Prevention

When it happens

Trigger: The POST to ButtonListURL returns resp.StatusCode != http.StatusOK — e.g. WAF challenge (403), rate limit (429), server maintenance (503), or 404 after an API endpoint change.

Common situations: Anti-bot defenses flagging the API request (headers/cookies stale); hammering the API and getting 429; the site changing its API and returning 404/405; Cloudflare 5xx during incidents.

Related errors


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

Appendix: source

Thrown at plugin/susu/susu.go:359

		"post_id": {postID},
		"guest":   {""},
	}
	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, ButtonListURL, strings.NewReader(form.Encode()))
	if err != nil {
		return nil, fmt.Errorf("[susu] 创建按钮列表请求失败: %w", err)
	}
	setAPIHeaders(req, fmt.Sprintf("%s/%s.html", BaseURL, postID))

	resp, err := p.doRequestWithRetry(client, req, MaxRetries)
	if err != nil {
		return nil, fmt.Errorf("[susu] 获取按钮列表失败: %w", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("[susu] 按钮列表请求返回状态码: %d", resp.StatusCode)
	}

	respBody, err := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
	if err != nil {
		return nil, fmt.Errorf("[susu] 读取按钮列表失败: %w", err)
	}

	var groups []downloadGroup
	if err := json.Unmarshal(respBody, &groups); err != nil {
		return nil, fmt.Errorf("[susu] 解析按钮列表失败: %w", err)
	}

	totalButtons := 0
	for _, group := range groups {
		totalButtons += len(group.Button)
	}
	if totalButtons == 0 {
		return nil, fmt.Errorf("[susu] 帖子 %s 没有可用下载按钮", postID)

View on GitHub (pinned to beaa561337)