fish2018/pansou · error

[ ] failed to parse response on page

Error message

[%s] failed to parse response on page %d: %w

What it means

jsonutil.Unmarshal failed to decode the search page body into SearchResponse. If no earlier pages produced results the search fails outright with this wrapped error; otherwise the loop breaks and partial results are returned.

Solutions

  1. Log the first bytes of the body on unmarshal failure to see what was actually returned
  2. Validate the JSON structure of the current API response against the SearchResponse struct
  3. Handle Cloudflare challenge pages (upgrade cloudscraper / use cookies)
  4. If the schema changed, update the SearchResponse/Discussion struct tags

Example fix

// before
return nil, fmt.Errorf("[%s] failed to parse response on page %d: %w", p.Name(), page, err)
// after
if jsonutil.Valid(body) {
    log.Printf("[%s] unexpected schema: %.200s", p.Name(), body)
} else {
    log.Printf("[%s] non-JSON response (Cloudflare?): %.200s", p.Name(), body)
}
return nil, fmt.Errorf("[%s] failed to parse response on page %d: %w", p.Name(), page, err)
Defensive patterns

Strategy: fallback

Validate before calling

resp, _ := http.Get("https://www.panzun.cc/api/discussions?page[size]=1")
b, _ := io.ReadAll(resp.Body)
if !json.Valid(b) { /* schema/anti-bot issue, skip plugin */ }

Type guard

func looksLikeFlarumResponse(body []byte) bool {
    var probe struct { Data []json.RawMessage `json:"data"` }
    return json.Unmarshal(body, &probe) == nil
}

Try / catch

results, err := plugin.Search(keyword, ext)
if err != nil {
    if strings.Contains(err.Error(), "failed to parse response") {
        // upstream schema change or Cloudflare page: log body snippet, fall back to other plugins
    }
}

Prevention

When it happens

Trigger: The panzun.cc discussions endpoint returns HTML (Cloudflare interstitial or error page), an empty body, or a changed JSON schema instead of the expected {links,data,included} Flarum JSONAPI payload on the first page.

Common situations: Cloudflare served a challenge page instead of JSON; site updated its API response shape; API returned an error JSON without the expected fields; CDN error page (502 HTML).

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/20446264d54af729. Report an issue: GitHub.

Appendix: source

Thrown at plugin/panzun/panzun.go:152

			}
			return nil, fmt.Errorf("[%s] unexpected status code: %d on page %d", p.Name(), resp.StatusCode, page)
		}

		body, err := io.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			if len(allResults) > 0 {
				break
			}
			return nil, fmt.Errorf("[%s] failed to read response on page %d: %w", p.Name(), page, err)
		}

		var searchResp SearchResponse
		if err := jsonutil.Unmarshal(body, &searchResp); err != nil {
			if len(allResults) > 0 {
				break
			}
			return nil, fmt.Errorf("[%s] failed to parse response on page %d: %w", p.Name(), page, err)
		}

		pageResults, err := p.convertDiscussionsToResults(client, searchResp.Data)
		if err != nil {
			fmt.Printf("[%s] Warning: detail parse failed on page %d: %v\n", p.Name(), page, err)
		}

		for _, result := range pageResults {
			if result.UniqueID == "" || seenIDs[result.UniqueID] {
				continue
			}
			seenIDs[result.UniqueID] = true
			allResults = append(allResults, result)
		}

		if searchResp.Links["next"] == "" {
			break
		}

View on GitHub (pinned to beaa561337)