{"record":{"id":"de3e4895caa25857","repo":"fish2018/pansou","slug":"s-w-de3e48","errorCode":null,"errorMessage":"[%s] 解析搜索结果失败: %w","messagePattern":"\\[(.+?)\\] 解析搜索结果失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/leso/leso.go","lineNumber":157,"sourceCode":"\tctx, cancel := context.WithTimeout(context.Background(), requestTimeout)\n\tdefer cancel()\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+\"/search.php?searchsubmit=yes\", strings.NewReader(form.Encode()))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建搜索请求失败: %w\", p.Name(), err)\n\t}\n\tsetHeaders(req, baseURL+\"/\")\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求返回 HTTP %d\", p.Name(), resp.StatusCode)\n\t}\n\tdoc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, 6<<20))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 解析搜索结果失败: %w\", p.Name(), err)\n\t}\n\treturn doc, nil\n}\n\nfunc (p *Plugin) fetchDetail(client *http.Client, item searchItem) (model.SearchResult, bool) {\n\tctx, cancel := context.WithTimeout(context.Background(), requestTimeout)\n\tdefer cancel()\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, item.detailURL, nil)\n\tif err != nil {\n\t\treturn model.SearchResult{}, false\n\t}\n\tsetHeaders(req, baseURL+\"/\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn model.SearchResult{}, false\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/leso/leso.go#L139-L175","documentation":"fetchSearch parses the response with goquery.NewDocumentFromReader over an io.LimitReader(resp.Body, 6<<20) and wraps failures with this message. Parsing fails when the body (capped at 6 MiB) is not valid HTML — JSON error, captcha page in another format, or corrupt/truncated stream.","triggerScenarios":"goquery.NewDocumentFromReader returns an error after reading at most 6 MiB of the response body: malformed HTML, non-HTML content type, or body cut off mid-stream (by the limit reader or proxy).","commonSituations":"Huge response truncated at 6 MiB producing unbalanced markup goquery can't finalize; anti-bot JSON challenge; misrouted response from a transparent proxy; encoding issues producing invalid bytes.","solutions":["Check Content-Type and log a body snippet to confirm what was actually returned","Raise or remove the 6 MiB LimitReader cap if truncation is the cause","Retry once on parse failure — transient network truncation often resolves","Validate encoding (GBK vs UTF-8 common on Discuz forums) and convert before parsing","If the site changed response format, update parsing or disable the mirror"],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, 6<<20))\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 解析搜索结果失败: %w\", p.Name(), err)\n}\n// after\nraw, err := io.ReadAll(io.LimitReader(resp.Body, 12<<20))\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 读取响应失败: %w\", p.Name(), err)\n}\ndoc, err := goquery.NewDocumentFromReader(bytes.NewReader(raw))\nif err != nil {\n    return nil, fmt.Errorf(\"[%s] 解析搜索结果失败 (len=%d): %w\", p.Name(), len(raw), err)\n}","handlingStrategy":"validation","validationCode":"ct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"text/html\") {\n    return fmt.Errorf(\"leso returned non-HTML: %q\", ct)\n}","typeGuard":null,"tryCatchPattern":"doc, err := plugin.fetchSearch(client, baseURL, keyword)\nif err != nil {\n    if strings.Contains(err.Error(), \"解析搜索结果失败\") {\n        // one clean retry often fixes transient truncation\n        if doc, retryErr := plugin.fetchSearch(client, baseURL, keyword); retryErr == nil {\n            return doc, nil\n        }\n    }\n    return err\n}","preventionTips":["Verify Content-Type before HTML parsing","Convert GBK/GB2312 responses to UTF-8 before parsing","Size the LimitReader cap above the largest expected page","Retry once on parse failure to absorb transient truncation","Disable mirrors that consistently return unparseable bodies"],"tags":["go","html-parsing","goquery","scraping"],"backgroundTag":"unexpected-response-shape","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"}