fish2018/pansou · error
[ ] 解析搜索 JSON 失败
Error message
[%s] 解析搜索 JSON 失败: %w
What it means
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.
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
Example fix
// before
type lou1SearchResponse struct {
OK bool `json:"ok"`
Data struct{ Hits []searchThread `json:"hits"` } `json:"data"`
}
// after — match renamed upstream fields
type lou1SearchResponse struct {
OK bool `json:"success"`
Data struct{ Hits []searchThread `json:"results"` } `json:"data"`
} Defensive patterns
Strategy: try-catch
Validate before calling
// before parsing
c.Header.Set("Accept", "application/json") Try / catch
results, err := lou1.Search(kw)
if err != nil && strings.Contains(err.Error(), "解析搜索 JSON 失败") {
if bytes.HasPrefix(body, []byte("<")) {
log.Printf("lou1 returned HTML — anti-bot page or endpoint moved")
}
return err
} Prevention
- 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
When it happens
Trigger: Body is not valid JSON or its structure does not match lou1SearchResponse fields (types or nesting differ).
Common situations: 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.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
AI-assisted analysis of fish2018/pansou@beaa561337 (2026-09-07).
Data as JSON: /api/errors/19c6cebc5beac407.
Report an issue: GitHub.
Appendix: source
Thrown at plugin/lou1/lou1.go:207
resp, err := p.doRequestWithRetry(req, client, maxRequestRetries)
if err != nil {
return nil, fmt.Errorf("[%s] 搜索请求失败: %w", p.Name(), err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("[%s] 搜索返回状态码: %d", p.Name(), resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("[%s] 读取搜索响应失败: %w", p.Name(), err)
}
var payload lou1SearchResponse
if err := jsonutil.Unmarshal(body, &payload); err != nil {
return nil, fmt.Errorf("[%s] 解析搜索 JSON 失败: %w", p.Name(), err)
}
if !payload.OK {
return nil, fmt.Errorf("[%s] 搜索接口返回失败", p.Name())
}
threads := make([]searchThread, 0, minInt(searchLimit, len(payload.Data.Hits)))
for _, hit := range payload.Data.Hits {
if len(threads) >= searchLimit {
break
}
title := strings.TrimSpace(hit.Subject)
threadURL := toAbsoluteURL(hit.ThreadURL)
if title == "" || threadURL == "" {
continue
}
threads = append(threads, searchThread{
Title: title,
URL: threadURL,View on GitHub (pinned to beaa561337)