{"record":{"id":"1b8c65ab76beefbf","repo":"fish2018/pansou","slug":"w-1b8c65","errorCode":null,"errorMessage":"解析响应失败: %w","messagePattern":"解析响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/quark4k/quark4k.go","lineNumber":242,"sourceCode":"\t\t}\n\t\t\n\t\t// 状态码检查\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tif i == p.retries {\n\t\t\t\treturn nil, false, fmt.Errorf(\"API返回非200状态码: %d\", resp.StatusCode)\n\t\t\t}\n\t\t\ttime.Sleep(500 * time.Millisecond)\n\t\t\tcontinue\n\t\t}\n\t\t\n\t\t// 请求成功，跳出重试循环\n\t\tbreak\n\t}\n\t\n\t// 解析响应\n\tvar apiResp Quark4KResponse\n\tif err := json.Unmarshal(responseBody, &apiResp); err != nil {\n\t\treturn nil, false, fmt.Errorf(\"解析响应失败: %w\", err)\n\t}\n\t\n\t// 处理结果\n\tresults := make([]model.SearchResult, 0, len(apiResp.Data))\n\t\n\t// 从included数组中提取posts，创建帖子ID到帖子内容的映射\n\tpostMap := make(map[string]Quark4KPost)\n\tfor _, item := range apiResp.Included {\n\t\t// 只处理posts类型\n\t\tif item.Type == \"posts\" {\n\t\t\t// 将整个item转换为JSON字节，然后解析为Quark4KPost结构\n\t\t\titemBytes, err := json.Marshal(item)\n\t\t\tif err == nil {\n\t\t\t\tvar post Quark4KPost\n\t\t\t\tif err := json.Unmarshal(itemBytes, &post); err == nil {\n\t\t\t\t\tpostMap[post.ID] = post\n\t\t\t\t}\n\t\t\t}","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/quark4k/quark4k.go#L224-L260","documentation":"After fetching, fetchPage unmarshals the response body into Quark4KResponse; a JSON decode failure is wrapped as \"解析响应失败\". The body did not match the expected search-API JSON shape (data/included arrays), typically because the server returned an error page or a changed schema.","triggerScenarios":"json.Unmarshal(responseBody, &apiResp) fails — body is HTML (WAF page), truncated, empty, or the API changed field types the struct cannot absorb (e.g. string where object expected).","commonSituations":"Cloudflare/anti-bot HTML page returned with 200 status; API version bump changing the response schema; proxy injecting an error page; keyword producing an unusual encoded response.","solutions":["Log the first ~512 bytes of responseBody on failure to see whether it's HTML or unexpected JSON.","Verify you got a 200 with real JSON (content-type check) before unmarshalling.","Update Quark4KResponse struct fields/types to match the current API schema.","Check the wrapped %w error — json.Unmarshal reports the exact offset/type mismatch."],"exampleFix":"// before\nvar apiResp Quark4KResponse\nif err := json.Unmarshal(responseBody, &apiResp); err != nil {\n    return nil, false, fmt.Errorf(\"解析响应失败: %w\", err)\n}\n// after\nif !utf8.Valid(responseBody) || json.Valid(responseBody) == false {\n    return nil, false, fmt.Errorf(\"解析响应失败: non-JSON body: %.200q\", responseBody)\n}\nvar apiResp Quark4KResponse\nif err := json.Unmarshal(responseBody, &apiResp); err != nil {\n    return nil, false, fmt.Errorf(\"解析响应失败: %w\", err)\n}","handlingStrategy":"type-guard","validationCode":"if len(responseBody) == 0 || !json.Valid(responseBody) {\n    return fmt.Errorf(\"response is not valid JSON (%d bytes)\", len(responseBody))\n}","typeGuard":"func isJSONBody(b []byte) bool {\n    trimmed := bytes.TrimSpace(b)\n    return len(trimmed) > 0 && trimmed[0] == '{' || (len(trimmed) > 0 && trimmed[0] == '[')\n}","tryCatchPattern":"results, _, err := fetchPage(...)\nif err != nil {\n    if strings.Contains(err.Error(), \"解析响应失败\") {\n        // dump body snippet, check for HTML/WAF page or schema drift\n    }\n}","preventionTips":["Check Content-Type is application/json before unmarshalling","Keep the Quark4KResponse struct in sync with the live API schema","Treat any HTML in a 200 response as an anti-bot signal, not a parse bug","Use json.Valid as a cheap pre-check before full unmarshal"],"tags":["json","parsing","api","scraping"],"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"}