{"record":{"id":"19c6cebc5beac407","repo":"fish2018/pansou","slug":"s-json-w-19c6ce","errorCode":null,"errorMessage":"[%s] 解析搜索 JSON 失败: %w","messagePattern":"\\[(.+?)\\] 解析搜索 JSON 失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/lou1/lou1.go","lineNumber":207,"sourceCode":"\n\tresp, err := p.doRequestWithRetry(req, client, maxRequestRetries)\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 != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取搜索响应失败: %w\", p.Name(), err)\n\t}\n\n\tvar payload lou1SearchResponse\n\tif err := jsonutil.Unmarshal(body, &payload); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 解析搜索 JSON 失败: %w\", p.Name(), err)\n\t}\n\tif !payload.OK {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索接口返回失败\", p.Name())\n\t}\n\n\tthreads := make([]searchThread, 0, minInt(searchLimit, len(payload.Data.Hits)))\n\tfor _, hit := range payload.Data.Hits {\n\t\tif len(threads) >= searchLimit {\n\t\t\tbreak\n\t\t}\n\t\ttitle := strings.TrimSpace(hit.Subject)\n\t\tthreadURL := toAbsoluteURL(hit.ThreadURL)\n\t\tif title == \"\" || threadURL == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tthreads = append(threads, searchThread{\n\t\t\tTitle: title,\n\t\t\tURL:   threadURL,","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/lou1/lou1.go#L189-L225","documentation":"The response body was read but jsonutil.Unmarshal could not decode it into lou1SearchResponse. The plugin expected a JSON search API payload; anything else (HTML error page, Cloudflare challenge, changed schema) triggers this error.","triggerScenarios":"Body is not valid JSON or its structure does not match lou1SearchResponse fields (types or nesting differ).","commonSituations":"WAF/anti-bot returned an HTML challenge instead of JSON; site changed the search API response shape; response is gzip-compressed and not decompressed; truncated body.","solutions":["Log the first bytes of body to see what actually came back (HTML challenge? partial JSON?)","Compare the live API response against the lou1SearchResponse struct; update fields/json tags if the schema changed","Confirm search URL points at the JSON API endpoint, not an HTML page","Check Content-Type and encoding handling (gzip) on the client transport"],"exampleFix":"// before\ntype lou1SearchResponse struct {\n    OK   bool `json:\"ok\"`\n    Data struct{ Hits []searchThread `json:\"hits\"` } `json:\"data\"`\n}\n// after — match renamed upstream fields\ntype lou1SearchResponse struct {\n    OK   bool `json:\"success\"`\n    Data struct{ Hits []searchThread `json:\"results\"` } `json:\"data\"`\n}","handlingStrategy":"try-catch","validationCode":"// before parsing\nc.Header.Set(\"Accept\", \"application/json\")","typeGuard":null,"tryCatchPattern":"results, err := lou1.Search(kw)\nif err != nil && strings.Contains(err.Error(), \"解析搜索 JSON 失败\") {\n    if bytes.HasPrefix(body, []byte(\"<\")) {\n        log.Printf(\"lou1 returned HTML — anti-bot page or endpoint moved\")\n    }\n    return err\n}","preventionTips":["Keep a saved golden JSON response as a parser regression test","Check Content-Type before unmarshaling","Log a body prefix on every decode failure for fast diagnosis","Handle gzip/encoding transparently in the transport"],"tags":["json","parsing","scraping"],"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"}