{"record":{"id":"9ac8aed9eb2352a1","repo":"fish2018/pansou","slug":"s-w-9ac8ae","errorCode":null,"errorMessage":"[%s] 解析首页失败: %w","messagePattern":"\\[(.+?)\\] 解析首页失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/jsnoteclub/jsnoteclub.go","lineNumber":246,"sourceCode":"\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, \"https://jsnoteclub.com/\", nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"[%s] 创建首页请求失败: %w\", p.Name(), err)\n\t}\n\tsetHTMLHeaders(req, \"https://jsnoteclub.com/\")\n\n\tresp, err := p.doRequestWithRetry(req, client, maxRequestRetries)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"[%s] 访问首页失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"[%s] 首页返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\tdoc, err := goquery.NewDocumentFromReader(resp.Body)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"[%s] 解析首页失败: %w\", p.Name(), err)\n\t}\n\n\tvar htmlBuilder strings.Builder\n\tdoc.Find(\"script\").Each(func(_ int, s *goquery.Selection) {\n\t\tif html, err := goquery.OuterHtml(s); err == nil {\n\t\t\thtmlBuilder.WriteString(html)\n\t\t}\n\t})\n\n\tmatch := dataKeyRegex.FindStringSubmatch(htmlBuilder.String())\n\tif len(match) < 2 {\n\t\treturn \"\", fmt.Errorf(\"[%s] 未能在首页找到 data-key\", p.Name())\n\t}\n\n\treturn match[1], nil\n}\n\nfunc (p *JsNoteClubPlugin) fetchPosts(client *http.Client, dataKey string) ([]ghostPost, error) {","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/jsnoteclub/jsnoteclub.go#L228-L264","documentation":"Returned when goquery.NewDocumentFromReader fails to parse the homepage response body as HTML. This wraps the goquery/html parsing error, which almost always means the body is empty, truncated, or not valid HTML (e.g. a gzip/brotli-encoded body that was not decompressed, or an error page in JSON/plain text).","triggerScenarios":"Response body is empty or closed mid-read, is binary/compressed content served without proper Content-Encoding handling, or the connection broke mid-body so the HTML parser hits malformed input it cannot recover from.","commonSituations":"Manually setting Accept-Encoding headers causing compressed bodies without decompression; a proxy returning a partial body; server streaming an error page in a non-HTML format; extremely large or truncated responses over flaky networks.","solutions":["Read and log a prefix of resp.Body before parsing to see what was actually received","Do not manually set Accept-Encoding unless you handle decompression; let net/http negotiate it","Check Content-Type of the response — ensure it is text/html","Retry the request; a truncated body is often transient","Verify the site still serves plain HTML rather than a JS-only shell with no HTML response"],"exampleFix":"// before\ndoc, err := goquery.NewDocumentFromReader(resp.Body)\nif err != nil {\n    return \"\", fmt.Errorf(\"[%s] 解析首页失败: %w\", p.Name(), err)\n}\n// after\nbody, readErr := io.ReadAll(resp.Body)\nif readErr != nil {\n    return \"\", fmt.Errorf(\"[%s] 读取首页失败: %w\", p.Name(), readErr)\n}\ndoc, err := goquery.NewDocumentFromReader(bytes.NewReader(body))\nif err != nil {\n    return \"\", fmt.Errorf(\"[%s] 解析首页失败: %w (body head: %.200s)\", p.Name(), err, body)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"key, err := plugin.fetchDataKey(client)\nif err != nil {\n    if strings.Contains(err.Error(), \"解析首页失败\") {\n        // HTML parse failure: capture body sample for diagnostics, then retry once\n        return retryWithBodyDump(err)\n    }\n    return err\n}","preventionTips":["Never set Accept-Encoding manually unless you decompress the body yourself","Validate Content-Type is text/html before parsing","Read the full body into memory first so you can log a sample on parse failure","Retry once on transient truncation before surfacing the error"],"tags":["html","parsing","goquery","scraping"],"backgroundTag":"invalid-json-response","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"}