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
- Log the first bytes of respBody when decoding fails to see whether it's HTML or JSON
- Compare the actual JSON structure against the MelostResponse struct; update struct fields/json tags after API changes
- If the body is an anti-bot HTML challenge, refresh cookies/tokens or update request fingerprint headers
- 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
- Check Content-Type and body prefix before unmarshaling
- Keep MelostResponse struct tags in sync with the live API schema
- Treat 200+HTML as an anti-bot signal and refresh cookies/fingerprint
- Snapshot real API responses in tests to catch schema drift
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)