{"record":{"id":"9a64dcc66c9270fe","repo":"fish2018/pansou","slug":"decode-response-failed-w-9a64dc","errorCode":null,"errorMessage":"decode response failed: %w","messagePattern":"decode response failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/melost/melost.go","lineNumber":187,"sourceCode":"\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"unexpected status code: %d\", resp.StatusCode)\n\t}\n\n\trespBody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read response failed: %w\", err)\n\t}\n\n\tvar apiResp MelostResponse\n\tif err := json.Unmarshal(respBody, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode response failed: %w\", err)\n\t}\n\n\tif apiResp.Code != 200 {\n\t\treturn nil, fmt.Errorf(\"api returned error: %s\", apiResp.Msg)\n\t}\n\n\treturn apiResp.Data.List, nil\n}\n\nfunc (p *MelostAsyncPlugin) deduplicateItems(items []MelostItem) []MelostItem {\n\tuniqueMap := make(map[string]MelostItem)\n\n\tfor _, item := range items {\n\t\tkey := item.DiskID\n\t\tif key == \"\" {\n\t\t\tkey = item.Link\n\t\t}\n\t\tif key == \"\" {","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/melost/melost.go#L169-L205","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nvar apiResp MelostResponse\nif err := json.Unmarshal(respBody, &apiResp); err != nil {\n\treturn nil, fmt.Errorf(\"decode response failed: %w\", err)\n}\n// after: surface body snippet for diagnosis\nvar apiResp MelostResponse\nif err := json.Unmarshal(respBody, &apiResp); err != nil {\n\treturn nil, fmt.Errorf(\"decode response failed: %w body=%q\", err, respBody[:min(len(respBody), 200)])\n}","handlingStrategy":"validation","validationCode":"ct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n\treturn nil, fmt.Errorf(\"expected JSON, got Content-Type: %s\", ct)\n}","typeGuard":"func looksLikeJSON(b []byte) bool {\n\tt := bytes.TrimSpace(b)\n\treturn len(t) > 0 && (t[0] == '{' || t[0] == '[')\n}","tryCatchPattern":"var apiResp MelostResponse\nif err := json.Unmarshal(respBody, &apiResp); err != nil {\n\t// 200 + HTML usually means an anti-bot challenge; log body and refresh session\n\treturn nil, fmt.Errorf(\"decode response failed: %w\", err)\n}","preventionTips":["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"],"tags":["json","schema","scraping","anti-bot"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"beaa56133755a548ebc51b090b3816e2ae044aa6","analyzedAt":"2026-09-07T00:31:18.025Z","contentChangedAt":"2026-09-07T00:31:18.025Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}