{"record":{"id":"7b8f12655709858b","repo":"fish2018/pansou","slug":"s-s-d-json-w-7b8f12","errorCode":null,"errorMessage":"[%s] %s网盘第%d页JSON解析失败: %w","messagePattern":"\\[(.+?)\\] (.+?)网盘第(.+?)页JSON解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/sdso/sdso.go","lineNumber":263,"sourceCode":"\treq.Header.Set(\"Connection\", \"keep-alive\")\n\treq.Header.Set(\"Referer\", \"https://sdso.top/\")\n\n\t// 5. 发送HTTP请求（带重试机制）\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页请求失败: %w\", p.Name(), fromType, pageNo, err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 6. 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页返回状态码: %d\", p.Name(), fromType, pageNo, resp.StatusCode)\n\t}\n\n\t// 7. 解析响应\n\tvar apiResp APIResponse\n\tif err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页JSON解析失败: %w\", p.Name(), fromType, pageNo, err)\n\t}\n\n\t// 8. 检查API响应状态\n\tif apiResp.Code != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页API错误: %s\", p.Name(), fromType, pageNo, apiResp.Msg)\n\t}\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[%s] %s网盘第%d页获取到 %d 个原始结果\\n\", p.Name(), fromType, pageNo, len(apiResp.Data.List))\n\t}\n\n\t// 9. 转换为标准格式\n\tresults := make([]model.SearchResult, 0, len(apiResp.Data.List))\n\tprocessedCount := 0\n\tskippedCount := 0\n\t\n\tfor i, item := range apiResp.Data.List {\n\t\t// 解密网盘链接","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/sdso/sdso.go#L245-L281","documentation":"The plugin received HTTP 200 but the response body could not be decoded into the expected APIResponse struct (json.Decode failed). This means sdso.top returned something other than the expected JSON envelope — an HTML page (challenge/block page), an error document, or a changed JSON schema.","triggerScenarios":"json.NewDecoder(resp.Body).Decode(&apiResp) errors after a 200 response: the body is HTML (Cloudflare/anti-bot interstitial), empty, truncated by a dropped connection, or the APIResponse struct fields no longer match the site's JSON.","commonSituations":"Cloudflare or similar serving a 200 HTML challenge page to the scraper; the site updated its API response format; a proxy/CDN returning an HTML error page with status 200; connection reset mid-body causing unexpected EOF.","solutions":["Enable DebugLog or capture the raw body when this happens to see what was actually returned.","If the body is HTML, the site is blocking the client — change IP, slow down, or update request headers.","Compare the actual JSON against the APIResponse struct and update struct field names/types after site changes.","Use json.NewDecoder with a size limit and check Content-Type is application/json before decoding.","Retry transient truncation errors (unexpected EOF) via the existing retry mechanism."],"exampleFix":"// before\nvar apiResp APIResponse\nif err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { return nil, err }\n// after\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n    return nil, fmt.Errorf(\"unexpected content-type %q, likely blocked\", ct)\n}\nvar apiResp APIResponse\nif err := json.NewDecoder(io.LimitReader(resp.Body, 1<<20)).Decode(&apiResp); err != nil { return nil, err }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(ctx, keyword)\nif err != nil && strings.Contains(err.Error(), \"JSON解析失败\") {\n    log.Printf(\"sdso returned non-JSON body (likely blocked or API changed): %v\", err)\n}","preventionTips":["Check Content-Type is application/json before decoding","Capture a sample body when decode fails to diagnose blocking vs schema drift","Keep APIResponse structs in sync with the live API","Decode with io.LimitReader to avoid unbounded bodies"],"tags":["json","parsing","http","scraping","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"}