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
- Log the raw body (truncated) alongside the unmarshal error to see what was actually returned
- Verify FetchAPIResponse struct matches the current API schema
- Check whether haisou.cc is serving HTML/captcha to your IP (curl the endpoint)
- 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
- Detect HTML/anti-bot pages before unmarshalling
- Keep FetchAPIResponse in sync with the live API schema
- Log truncated bodies on failure for diagnosis
- Treat repeated parse failures as an upstream health signal and back off
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)