{"record":{"id":"48fce293b2edc331","repo":"fish2018/pansou","slug":"w-48fce2","errorCode":null,"errorMessage":"解析响应失败: %w","messagePattern":"解析响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/yingso/yingso.go","lineNumber":319,"sourceCode":"\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\n\tdata, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize+1))\n\tif err != nil {\n\t\treturn err\n\t}\n\tif len(data) > maxResponseSize {\n\t\treturn fmt.Errorf(\"响应超过 %d 字节\", maxResponseSize)\n\t}\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"HTTP %d: %s\", resp.StatusCode, strings.TrimSpace(string(data)))\n\t}\n\tif err := jsonutil.Unmarshal(data, target); err != nil {\n\t\treturn fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc encryptPayload(payload interface{}, config apiConfig) (encryptedPayload, error) {\n\tno, err := randomToken(24)\n\tif err != nil {\n\t\treturn encryptedPayload{}, err\n\t}\n\tif config.Start < 0 || config.End <= config.Start || config.End > len(no) {\n\t\treturn encryptedPayload{}, fmt.Errorf(\"无效的加密区间 %d:%d\", config.Start, config.End)\n\t}\n\n\tencoded, err := jsonutil.MarshalString(payload)\n\tif err != nil {\n\t\treturn encryptedPayload{}, err\n\t}\n\treturn encryptedPayload{","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/yingso/yingso.go#L301-L337","documentation":"requestJSON in plugin/yingso wraps any failure from jsonutil.Unmarshal of the HTTP response body with '解析响应失败: %w'. It is thrown after a 200 status is confirmed, so it means the server returned 200 with a body that is not valid JSON (or not JSON at all, e.g. HTML). The underlying unmarshal error is preserved via %w for errors.Is/As inspection.","triggerScenarios":"yingso fetchConfig/search/resolveItem receive a 200 response whose body fails jsonutil.Unmarshal — typically HTML error/login pages served with status 200, truncated or maxResponseSize-clipped bodies, or an API shape change.","commonSituations":"The upstream site serves a Cloudflare/anti-bot HTML challenge with 200; the site changed its JSON schema; a proxy or captive portal returns HTML; the response was cut off at maxResponseSize bytes mid-JSON.","solutions":["Print/log the raw response body (data) to see what was actually returned — usually HTML from an anti-bot page","Re-run the request with browser-like headers (User-Agent, Cookie) to bypass the HTML challenge page","Check whether the upstream API response schema changed and update the target struct to match","If truncation is the cause, raise maxResponseSize and re-check Content-Length handling","Retry on transient gateway corruption"],"exampleFix":"// before\nif err := jsonutil.Unmarshal(data, target); err != nil {\n    return fmt.Errorf(\"解析响应失败: %w\", err)\n}\n// after\nif err := jsonutil.Unmarshal(data, target); err != nil {\n    return fmt.Errorf(\"解析响应失败: %w (body=%s)\", err, truncate(data, 200))\n}","handlingStrategy":"try-catch","validationCode":"if !json.Valid(body) { return fmt.Errorf(\"响应不是有效 JSON\") }","typeGuard":"func isHTMLBody(b []byte) bool { t := bytes.TrimSpace(b); return len(t) > 0 && t[0] == '<' }","tryCatchPattern":"if err := jsonutil.Unmarshal(data, target); err != nil {\n    var uErr *json.UnmarshalTypeError\n    if errors.As(err, &uErr) { /* log field/type mismatch */ }\n    log.Printf(\"非 JSON 响应: %s\", truncate(data, 200))\n    return fmt.Errorf(\"解析响应失败: %w\", err)\n}","preventionTips":["Log the raw body on unmarshal failure to spot anti-bot HTML pages quickly","Send browser-like User-Agent/Cookie headers to avoid 200 HTML challenges","Keep maxResponseSize comfortably above the largest expected response","Pin/monitor the upstream API schema; update structs when the site changes"],"tags":["json","http","unmarshal","upstream"],"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"}