fish2018/pansou · error

decode response failed

Error message

decode response failed: %w

What it means

searchPage unmarshals the response body into MelostResponse; if json.Unmarshal fails, it returns "decode response failed: %w". This means melost.cn returned 200 but the body is not valid JSON or does not match the MelostResponse struct (e.g. an HTML error page or changed schema).

Solutions

  1. Log the first bytes of respBody when decoding fails to see whether it's HTML or JSON
  2. Compare the actual JSON structure against the MelostResponse struct; update struct fields/json tags after API changes
  3. If the body is an anti-bot HTML challenge, refresh cookies/tokens or update request fingerprint headers
  4. Add Content-Type checking before unmarshaling to fail fast on non-JSON responses

Example fix

// before
var apiResp MelostResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
	return nil, fmt.Errorf("decode response failed: %w", err)
}
// after: surface body snippet for diagnosis
var apiResp MelostResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
	return nil, fmt.Errorf("decode response failed: %w body=%q", err, respBody[:min(len(respBody), 200)])
}
Defensive patterns

Strategy: validation

Validate before calling

ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
	return nil, fmt.Errorf("expected JSON, got Content-Type: %s", ct)
}

Type guard

func looksLikeJSON(b []byte) bool {
	t := bytes.TrimSpace(b)
	return len(t) > 0 && (t[0] == '{' || t[0] == '[')
}

Try / catch

var apiResp MelostResponse
if err := json.Unmarshal(respBody, &apiResp); err != nil {
	// 200 + HTML usually means an anti-bot challenge; log body and refresh session
	return nil, fmt.Errorf("decode response failed: %w", err)
}

Prevention

When it happens

Trigger: The 200 response body is HTML (WAF challenge page served with 200), truncated/malformed JSON, or the API response schema changed so fields no longer match MelostResponse types.

Common situations: Anti-bot systems returning a 200 HTML challenge, melost.cn deploying an API version change that alters the response shape, or a CDN/error page returned with status 200.

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/9a64dcc66c9270fe. Report an issue: GitHub.

Appendix: source

Thrown at plugin/melost/melost.go:187

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("read response failed: %w", err)
	}

	var apiResp MelostResponse
	if err := json.Unmarshal(respBody, &apiResp); err != nil {
		return nil, fmt.Errorf("decode response failed: %w", err)
	}

	if apiResp.Code != 200 {
		return nil, fmt.Errorf("api returned error: %s", apiResp.Msg)
	}

	return apiResp.Data.List, nil
}

func (p *MelostAsyncPlugin) deduplicateItems(items []MelostItem) []MelostItem {
	uniqueMap := make(map[string]MelostItem)

	for _, item := range items {
		key := item.DiskID
		if key == "" {
			key = item.Link
		}
		if key == "" {

View on GitHub (pinned to beaa561337)