{"record":{"id":"18bd254a0a8f361a","repo":"fish2018/pansou","slug":"s-json-w-18bd25","errorCode":null,"errorMessage":"[%s] JSON解析失败: %w","messagePattern":"\\[(.+?)\\] JSON解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/meitizy/meitizy.go","lineNumber":191,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求HTTP状态错误: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\t// 读取响应体\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取响应体失败: %w\", p.Name(), err)\n\t}\n\n\t// 解析JSON响应\n\tvar apiResp searchResponse\n\tif err := json.Unmarshal(body, &apiResp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n\t}\n\n\t// 转换为标准格式\n\tresults := p.convertToSearchResults(apiResp.Data)\n\n\t// 关键词过滤（标准网盘插件需要过滤）\n\tfilteredResults := plugin.FilterResultsByKeyword(results, keyword)\n\n\treturn filteredResults, nil\n}\n\n// convertToSearchResults 将API响应转换为标准SearchResult格式\nfunc (p *MeitizyPlugin) convertToSearchResults(items []apiItem) []model.SearchResult {\n\tresults := make([]model.SearchResult, 0, len(items))\n\n\tfor _, item := range items {\n\t\t// Skip malformed or empty links returned by the API.\n\t\tlinkURL := strings.TrimSpace(item.Link)","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/meitizy/meitizy.go#L173-L209","documentation":"After reading the body, searchImpl unmarshals it into the searchResponse struct with json.Unmarshal; failure produces this wrapped error. It means the 200 response body was not the expected JSON shape — typically an HTML anti-bot/interstitial page or an envelope whose schema changed.","triggerScenarios":"Response is valid HTTP 200 but not parseable into searchResponse: HTML challenge page, plain-text error, empty body, or API response fields renamed/retyped after a site update so types no longer match.","commonSituations":"WAF serves a 200 'checking your browser' page; the API updated its JSON envelope (field renames, data changed from array to object); a CDN returns an HTML error page with status 200.","solutions":["Log the wrapped %w error and a snippet of body to see what was actually returned","Check whether the body is HTML (WAF page) and update Origin/Referer/User-Agent or add cookie handling to pass the challenge","Diff the live API JSON against the searchResponse struct and update field names/types/json tags","Use json.Decoder and tolerate schema drift by unmarshaling into json.RawMessage for volatile fields"],"exampleFix":"// before\nvar apiResp searchResponse\nif err := json.Unmarshal(body, &apiResp); err != nil {\n    return nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n}\n// after\nvar apiResp searchResponse\nif err := json.Unmarshal(body, &apiResp); err != nil {\n    return nil, fmt.Errorf(\"[%s] JSON解析失败(响应: %.200s): %w\", p.Name(), string(body), err)\n}","handlingStrategy":"validation","validationCode":"trimmed := bytes.TrimSpace(body)\nif len(trimmed) == 0 || (trimmed[0] != '{' && trimmed[0] != '[') {\n    // not JSON (likely an HTML challenge page); skip unmarshal\n    return nil, fmt.Errorf(\"non-JSON response: %.100s\", string(trimmed))\n}","typeGuard":"func looksLikeJSON(b []byte) bool {\n    t := bytes.TrimSpace(b)\n    return len(t) > 0 && (t[0] == '{' || t[0] == '[')\n}","tryCatchPattern":"if err != nil {\n    var synErr *json.SyntaxError\n    if errors.As(err, &synErr) {\n        log.Printf(\"bad JSON at offset %d, body head: %.200s\", synErr.Offset, string(body))\n    }\n    return nil, err\n}","preventionTips":["Check body starts with '{' or '[' before unmarshaling to catch HTML challenge pages","Keep searchResponse struct json tags in sync with the live API (test against a recorded fixture)","Never assume a 200 status means valid JSON — always validate shape","Pin/verify the API version and update struct definitions after site changes"],"tags":["json","parsing","api","schema"],"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"}