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

  1. Log the first bytes of body to see what actually came back (HTML challenge? partial JSON?)
  2. Compare the live API response against the lou1SearchResponse struct; update fields/json tags if the schema changed
  3. Confirm search URL points at the JSON API endpoint, not an HTML page
  4. 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

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)