fish2018/pansou · error

[ ] hsid= 链接请求失败

Error message

[%s] hsid=%s链接请求失败: %w

What it means

This error is thrown by fetchShareLink when the share-link HTTP request fails after the plugin's built-in retries (p.doRequestWithRetry), e.g. connection errors, TLS failures, DNS failures, or context timeout. The wrapped err carries the retry exhaustion message ('重试 %d 次后仍然失败') from doRequestWithRetry.

Solutions

  1. Run curl https://haisou.cc/ from the same host to confirm connectivity
  2. Check whether the host needs a proxy (set HTTP(S)_PROXY or configure the http.Client Transport)
  3. Increase the 15-second timeout and/or maxRetries in doRequestWithRetry
  4. Inspect the wrapped error from doRequestWithRetry to distinguish timeout vs connection refused vs TLS
  5. Verify DNS resolution inside your deployment environment (docker/container networks).

Example fix

// before
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
	return "", "", fmt.Errorf("[%s] hsid=%s链接请求失败: %w", p.Name(), hsid, err)
}
// after
resp, err := p.doRequestWithRetry(req, client)
if err != nil {
	var netErr net.Error
	if errors.As(err, &netErr) && netErr.Timeout() {
		return "", "", fmt.Errorf("[%s] hsid=%s链接请求超时(15s),可考虑增大超时或稍后重试: %w", p.Name(), hsid, err)
	}
	return "", "", fmt.Errorf("[%s] hsid=%s链接请求失败: %w", p.Name(), hsid, err)
}
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight connectivity check
resp, err := http.Head("https://haisou.cc/")
if err != nil {
	return fmt.Errorf("upstream unreachable: %w", err)
}
resp.Body.Close()

Try / catch

resp, err := p.doRequestWithRetry(req, client)
if err != nil {
	var nerr net.Error
	if errors.As(err, &nerr) && nerr.Timeout() {
		// schedule retry with backoff
	}
	return fmt.Errorf("share link request failed: %w", err)
}
defer resp.Body.Close()

Prevention

When it happens

Trigger: p.doRequestWithRetry(req, client) returns a non-nil error inside fetchShareLink — the GET to the haisou fetch API failed on every retry attempt (network unreachable, timeout after 15s, TLS handshake failure, connection reset).

Common situations: haisou.cc is down or blocked by firewall/GFW; server or corporate proxy blocking outbound HTTPS; 15s timeout too short for a slow network; rate limiting causing connection resets; DNS resolution failures in containers.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at plugin/haisou/haisou.go:403

	defer cancel()

	// 创建请求对象
	req, err := http.NewRequestWithContext(ctx, "GET", fetchURL, nil)
	if err != nil {
		return "", "", fmt.Errorf("[%s] hsid=%s创建链接请求失败: %w", p.Name(), hsid, err)
	}

	// 设置请求头
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Referer", "https://haisou.cc/")

	// 发送HTTP请求(带重试机制)
	resp, err := p.doRequestWithRetry(req, client)
	if err != nil {
		return "", "", fmt.Errorf("[%s] hsid=%s链接请求失败: %w", p.Name(), hsid, err)
	}
	defer resp.Body.Close()

	// 检查状态码
	if resp.StatusCode != 200 {
		return "", "", fmt.Errorf("[%s] hsid=%s链接请求返回状态码: %d", p.Name(), hsid, resp.StatusCode)
	}

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", "", fmt.Errorf("[%s] hsid=%s读取响应失败: %w", p.Name(), hsid, err)
	}

	// 解析响应
	var apiResp FetchAPIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return "", "", fmt.Errorf("[%s] hsid=%s链接JSON解析失败: %w", p.Name(), hsid, err)

View on GitHub (pinned to beaa561337)