fish2018/pansou · error

[ ] hsid= 链接JSON解析失败

Error message

[%s] hsid=%s链接JSON解析失败: %w

What it means

This error is thrown by fetchShareLink when the fetched share-link response body cannot be unmarshalled into FetchAPIResponse. Like the search-page variant, it means the upstream returned malformed or structurally unexpected JSON (often an HTML/anti-bot page) despite HTTP 200.

Solutions

  1. Log the raw body (truncated) alongside the unmarshal error to see what was actually returned
  2. Verify FetchAPIResponse struct matches the current API schema
  3. Check whether haisou.cc is serving HTML/captcha to your IP (curl the endpoint)
  4. Sniff the first non-whitespace byte — non-'{' bodies are HTML, skip unmarshalling and retry/flag

Example fix

// before
var apiResp FetchAPIResponse
if err := json.Unmarshal(body, &apiResp); err != nil {
	return "", "", fmt.Errorf("[%s] hsid=%s链接JSON解析失败: %w", p.Name(), hsid, err)
}
// after
trimmed := bytes.TrimSpace(body)
if len(trimmed) == 0 || trimmed[0] != '{' {
	return "", "", fmt.Errorf("[%s] hsid=%s返回非JSON响应(疑似反爬页面): %.100s", p.Name(), hsid, string(trimmed))
}
var apiResp FetchAPIResponse
if err := json.Unmarshal(trimmed, &apiResp); err != nil {
	return "", "", fmt.Errorf("[%s] hsid=%s链接JSON解析失败: %w", p.Name(), hsid, err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

trimmed := bytes.TrimSpace(body)
if len(trimmed) == 0 || trimmed[0] != '{' {
	return errors.New("response is not JSON (likely HTML/anti-bot page)")
}
if !json.Valid(trimmed) {
	return errors.New("response body is not valid JSON")
}

Type guard

func isJSONBody(b []byte) bool {
	t := bytes.TrimSpace(b)
	return len(t) > 0 && t[0] == '{' && json.Valid(t)
}

Try / catch

var apiResp FetchAPIResponse
if err := json.Unmarshal(body, &apiResp); err != nil {
	log.Printf("share link parse failed, head: %.200s", body)
	return fmt.Errorf("share link JSON parse failed: %w", err)
}

Prevention

When it happens

Trigger: json.Unmarshal(body, &apiResp) fails in fetchShareLink — body is not valid JSON for FetchAPIResponse (wrong types, truncated body, HTML error content).

Common situations: Anti-bot/WAF page returned with HTTP 200; API schema drift (field type changed); response truncated by proxy; Captcha challenge page served as HTML.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at plugin/haisou/haisou.go:421

		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)
	}

	// 检查API响应状态
	if apiResp.Code != 0 {
		return "", "", fmt.Errorf("[%s] hsid=%s链接API错误: %s", p.Name(), hsid, apiResp.Msg)
	}

	// 根据平台类型构建完整的分享链接
	shareURL := buildShareURL(platform, apiResp.Data.ShareCode)
	if shareURL == "" {
		return "", "", fmt.Errorf("[%s] hsid=%s不支持的网盘平台: %s", p.Name(), hsid, platform)
	}

	// 获取密码
	password := ""
	if apiResp.Data.SharePwd != nil {
		password = *apiResp.Data.SharePwd
	}

View on GitHub (pinned to beaa561337)