{"record":{"id":"b448a4db2b569b1a","repo":"fish2018/pansou","slug":"json-w-b448a4","errorCode":null,"errorMessage":"下载链接JSON解析失败: %w","messagePattern":"下载链接JSON解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/cyg/cyg.go","lineNumber":257,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"下载链接请求失败: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"下载链接请求状态码: %d\", resp.StatusCode)\n\t}\n\n\t// 解析响应\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"读取下载链接响应失败: %w\", err)\n\t}\n\n\tvar downloadData []CygDownload\n\tif err := json.Unmarshal(body, &downloadData); err != nil {\n\t\treturn nil, fmt.Errorf(\"下载链接JSON解析失败: %w\", err)\n\t}\n\n\t// 转换为model.Link格式\n\treturn p.convertToLinks(downloadData), nil\n}\n\n// convertToSearchResult 转换为标准搜索结果格式\nfunc (p *CygPlugin) convertToSearchResult(post CygPost, links []model.Link) model.SearchResult {\n\treturn model.SearchResult{\n\t\tUniqueID: fmt.Sprintf(\"cyg-%d\", post.ID),\n\t\tTitle:    p.cleanHTML(post.Title.Rendered),\n\t\tContent:  p.cleanHTML(post.Excerpt.Rendered),\n\t\tDatetime: p.parseDateTime(post.Date),\n\t\tTags:     []string{post.CategoryName},\n\t\tLinks:    links,\n\t\tChannel:  \"\", // 插件搜索结果必须为空字符串\n\t}\n}","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/cyg/cyg.go#L239-L275","documentation":"getDownloadLinks fails to json.Unmarshal the response body into []CygDownload and wraps the error with \"下载链接JSON解析失败\". It means the server replied 200 but the body is not the expected JSON array of download objects.","triggerScenarios":"The 200 response body is HTML (error page / anti-bot challenge), empty, or its JSON schema changed so it no longer unmarshals into []CygDownload.","commonSituations":"Site deployed a new version changing the download API response shape, WAF serving an HTML challenge with status 200, empty body due to upstream cache miss, or CDN returning a compressed body the client did not request.","solutions":["Dump the raw body (on unmarshal failure) to see what was actually returned.","Compare the response against the CygDownload struct and update its json field tags after a site schema change.","Check whether the endpoint now returns an object wrapper instead of a bare array and adjust the target type.","Verify Content-Type is application/json; if HTML, treat it as an anti-bot/upstream error rather than a parse bug."],"exampleFix":"// before\nvar downloadData []CygDownload\nif err := json.Unmarshal(body, &downloadData); err != nil {\n    return nil, fmt.Errorf(\"下载链接JSON解析失败: %w\", err)\n}\n// after\nvar downloadData []CygDownload\nif err := json.Unmarshal(body, &downloadData); err != nil {\n    return nil, fmt.Errorf(\"下载链接JSON解析失败: %w, body: %.200s\", err, body)\n}","handlingStrategy":"validation","validationCode":"ct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"json\") {\n    return fmt.Errorf(\"expected JSON, got %s\", ct)\n}","typeGuard":null,"tryCatchPattern":"links, err := p.getDownloadLinks(id)\nif err != nil && strings.Contains(err.Error(), \"JSON解析失败\") {\n    // response was not the expected schema; treat source as changed/broken\n    return nil, fmt.Errorf(\"source schema changed, plugin update needed: %w\", err)\n}","preventionTips":["Check Content-Type before assuming JSON.","Pin/verify the expected response shape after site updates.","Log a truncated body on unmarshal failure to speed up diagnosis."],"tags":["json","go","parsing","api"],"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"}