{"record":{"id":"a789725400dcea80","repo":"fish2018/pansou","slug":"s-w-a78972","errorCode":null,"errorMessage":"[%s] 解析搜索页面失败: %w","messagePattern":"\\[(.+?)\\] 解析搜索页面失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/zhizhen/zhizhen.go","lineNumber":229,"sourceCode":"\treq.Header.Set(\"Upgrade-Insecure-Requests\", \"1\")\n\treq.Header.Set(\"Cache-Control\", \"max-age=0\")\n\treq.Header.Set(\"Referer\", strings.TrimRight(baseURL, \"/\")+\"/\")\n\n\t// 5. 发送请求（带重试机制）\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\t// 6. 解析搜索结果页面\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 解析搜索页面失败: %w\", p.Name(), err)\n\t}\n\n\t// 7. 提取搜索结果\n\tvar results []model.SearchResult\n\n\tdoc.Find(\".module-search-item\").Each(func(i int, s *goquery.Selection) {\n\t\tresult := p.parseSearchItem(s, keyword)\n\t\tif result.UniqueID != \"\" {\n\t\t\tresults = append(results, result)\n\t\t}\n\t})\n\n\treturn results, nil\n}\n\n// parseSearchItem 解析单个搜索结果项\nfunc (p *ZhizhenAsyncPlugin) parseSearchItem(s *goquery.Selection, keyword string) model.SearchResult {\n\tresult := model.SearchResult{}","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/zhizhen/zhizhen.go#L211-L247","documentation":"This error means goquery could not parse the HTML body of a successful zhizhen search response into a document. NewDocumentFromReader fails on body read errors (truncated connection) or, rarely, empty/unreadable input. The request itself succeeded with status 200, so this points to a corrupt or empty response payload.","triggerScenarios":"goquery.NewDocumentFromReader(resp.Body) returns err in searchAtBase — connection reset mid-body, chunked encoding error, or empty body from the mirror.","commonSituations":"Unstable mirror closing connections early, proxy interference, gzip/deflate mis-negotiation, or the server replying 200 with an empty body under load.","solutions":["Read the body with io.ReadAll first and log its length/snippet to diagnose.","Re-run against another base URL — searchImpl's mirror fallback handles transient corruption.","Ensure the transport handles gzip (DefaultTransport does when Accept-Encoding is unset).","Retry the request; truncation is usually transient.","Check goquery version for known parsing issues."],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(resp.Body)\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 解析搜索页面失败: %w\", p.Name(), err)\n}\n// after\nbody, readErr := io.ReadAll(resp.Body)\nif readErr != nil {\n    return nil, fmt.Errorf(\"[%s] 读取搜索页面失败: %w\", p.Name(), readErr)\n}\ndoc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"results, err := searchAtBase(client, baseURL, keyword)\nif err != nil {\n    if strings.Contains(err.Error(), \"解析搜索页面失败\") {\n        return searchAtBase(client, otherBaseURL, keyword) // corrupt body — retry other mirror\n    }\n    return nil, err\n}","preventionTips":["Read body into memory before parsing to detect truncation","Treat empty bodies as explicit errors","Retry parse failures against another mirror","Verify gzip handling when using custom Transports"],"tags":["parsing","html","goquery","http"],"backgroundTag":"json-parse-error","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"}