{"record":{"id":"44996006254fe939","repo":"fish2018/pansou","slug":"s-s-d-json-w","errorCode":null,"errorMessage":"[%s] %s网盘第%d页JSON解析失败: %w","messagePattern":"\\[(.+?)\\] (.+?)网盘第(.+?)页JSON解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/haisou/haisou.go","lineNumber":359,"sourceCode":"\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页请求失败: %w\", p.Name(), panType, pageNo, err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页返回状态码: %d\", p.Name(), panType, pageNo, resp.StatusCode)\n\t}\n\n\t// 读取响应体\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页读取响应失败: %w\", p.Name(), panType, pageNo, err)\n\t}\n\n\t// 解析响应\n\tvar apiResp SearchAPIResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页JSON解析失败: %w\", p.Name(), panType, pageNo, err)\n\t}\n\n\t// 检查API响应状态\n\tif apiResp.Code != 0 {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页API错误: %s\", p.Name(), panType, pageNo, apiResp.Msg)\n\t}\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[%s] %s网盘第%d页获取到 %d 个搜索结果\\n\", p.Name(), panType, pageNo, len(apiResp.Data.List))\n\t}\n\n\treturn apiResp.Data.List, nil\n}\n\n// fetchShareLink 通过hsid获取具体的分享链接\nfunc (p *HaisouPlugin) fetchShareLink(client *http.Client, hsid string, platform string) (string, string, error) {\n\t// 构建获取链接的URL\n\tfetchURL := fmt.Sprintf(\"https://haisou.cc/api/pan/share/%s/fetch\", hsid)","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/haisou/haisou.go#L341-L377","documentation":"This error is thrown by fetchSearchPage in the haisou (海搜) plugin when the JSON body returned by the upstream search API cannot be unmarshalled into SearchAPIResponse. The error wraps the underlying json.Unmarshal error with the plugin name, the pan (cloud-drive) type, and the page number, so you know exactly which page of which drive failed. It indicates the upstream API returned malformed, truncated, or structurally unexpected JSON (e.g. an HTML error page instead of JSON).","triggerScenarios":"json.Unmarshal(body, &apiResp) fails in fetchSearchPage — the HTTP body fetched from the haisou search API is not valid JSON matching SearchAPIResponse, or violates JSON unmarshalling rules (wrong types, truncated response).","commonSituations":"Upstream haisou.cc API is degraded and returns an HTML block/captcha/anti-bot page; response body truncated by proxy or timeout; API contract change introducing new field types (e.g. string where number expected); rate limiting returning an error page with HTTP 200.","solutions":["Log the raw response body (fmt.Println(string(body))) before unmarshalling to see what the API actually returned","Check whether the upstream API is up and serving JSON (curl the endpoint manually)","Verify the SearchAPIResponse struct still matches the current API schema and update field types","Add retry/backoff on this page request in case the failure is transient","Use json.Decoder with better error context or tolerate unknown fields via json.RawMessage"],"exampleFix":"// before\nif err := json.Unmarshal(body, &apiResp); err != nil {\n\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页JSON解析失败: %w\", p.Name(), panType, pageNo, err)\n}\n// after\nif len(body) > 0 && body[0] != '{' {\n\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页返回非JSON内容(前100字节: %s)\", p.Name(), panType, pageNo, body[:min(100, len(body))])\n}\nif err := json.Unmarshal(body, &apiResp); err != nil {\n\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页JSON解析失败: %w (body=%s)\", p.Name(), panType, pageNo, err, body[:min(200, len(body))])\n}","handlingStrategy":"try-catch","validationCode":"if len(body) == 0 || bytes.TrimSpace(body)[0] != '{' {\n\treturn fmt.Errorf(\"upstream returned non-JSON content\")\n}\nif !json.Valid(body) {\n\treturn fmt.Errorf(\"body is not valid JSON\")\n}","typeGuard":"func looksLikeJSON(b []byte) bool {\n\tt := bytes.TrimSpace(b)\n\treturn len(t) > 0 && (t[0] == '{' || t[0] == '[') && json.Valid(t)\n}","tryCatchPattern":"var apiResp SearchAPIResponse\nif err := json.Unmarshal(body, &apiResp); err != nil {\n\tlog.Printf(\"search page parse failed, body head: %.200s\", body)\n\treturn fmt.Errorf(\"search JSON parse failed: %w\", err)\n}","preventionTips":["Log raw response bodies (truncated) on parse failure to diagnose upstream drift","Callers should treat parse errors as retryable and skip the failing page","Pin/verify the upstream API contract when upgrading the plugin","Check for anti-bot/HTML responses before unmarshalling"],"tags":["json","unmarshal","network","api-response","go"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}