fish2018/pansou · error

[susu] 创建按钮列表请求失败

Error message

[susu] 创建按钮列表请求失败: %w

What it means

getLinks throws this when http.NewRequestWithContext fails to construct the POST request to ButtonListURL with the encoded form body. This is a client-side request construction error: a malformed URL, an invalid method, or an unreadable body — it never leaves the process. NewRequest only errors on invalid URL parsing, unsupported method characters, or a broken reader.

Solutions

  1. Inspect ButtonListURL — validate it parses with url.Parse and is absolute (scheme + host).
  2. Check for accidental whitespace/control characters in the URL string or its format arguments (postID).
  3. Verify postID doesn't contain characters that break the Sprintf'd referer/URL construction.
  4. Add a startup-time validation of ButtonListURL so failures surface at config load, not per request.

Example fix

// before
req, err := http.NewRequestWithContext(ctx, http.MethodPost, ButtonListURL, strings.NewReader(form.Encode()))
// after: validate the URL first
if u, uerr := url.Parse(ButtonListURL); uerr != nil || u.Scheme == "" || u.Host == "" {
    return nil, fmt.Errorf("[susu] invalid ButtonListURL %q: %w", ButtonListURL, uerr)
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, ButtonListURL, strings.NewReader(form.Encode()))
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(ButtonListURL)
if err != nil || u.Scheme == "" || u.Host == "" {
    // refuse to call getLinks with a malformed endpoint
}

Try / catch

links, err := p.getLinks(postID)
if err != nil && strings.Contains(err.Error(), "创建按钮列表请求失败") {
    // configuration bug, not transient — do not retry
    log.Printf("invalid ButtonListURL configuration: %v", err)
}

Prevention

When it happens

Trigger: getLinks builds the button-list request and ButtonListURL is malformed/unparseable (invalid characters, bad scheme), or strings.NewReader(form.Encode()) yields a nil/broken body — NewRequestWithContext returns err immediately.

Common situations: ButtonListURL constant edited to an invalid URL; config-supplied URL with spaces or unencoded characters; empty/nil base URL combined via a bad join producing '://...'.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at plugin/susu/susu.go:349

}

// getLinks 获取网盘链接
func (p *SusuAsyncPlugin) getLinks(client *http.Client, postID string) ([]model.Link, error) {
	// 检查缓存
	if cachedLinks, ok := buttonListCache.Load(postID); ok {
		return cachedLinks.([]model.Link), nil
	}

	form := url.Values{
		"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

View on GitHub (pinned to beaa561337)