{"record":{"id":"ea5e8990096479a4","repo":"fish2018/pansou","slug":"s-json-w-ea5e89","errorCode":null,"errorMessage":"[%s] JSON解析失败: %w","messagePattern":"\\[(.+?)\\] JSON解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/nsgame/nsgame.go","lineNumber":179,"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] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\t// 6. 读取响应体\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// 7. 解析JSON响应\n\tvar apiResp NSGameResponse\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// 8. 检查响应状态\n\tif !apiResp.Success || (apiResp.Code != \"\" && apiResp.Code != \"200\") {\n\t\treturn nil, fmt.Errorf(\"[%s] API返回错误: success=%v, code=%s\", p.Name(), apiResp.Success, apiResp.Code)\n\t}\n\n\t// 9. 转换为标准格式\n\titems := apiResp.Data.PageData.Data\n\tvar results []model.SearchResult\n\tvar wg sync.WaitGroup\n\tvar mu sync.Mutex\n\tsem := make(chan struct{}, 8)\n\tfor _, item := range items {\n\t\titem := item\n\t\tif item.ID == 0 {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/nsgame/nsgame.go#L161-L197","documentation":"json.Unmarshal failed to decode the search response body into NSGameResponse. The plugin expects a JSON object with success/code/data fields; this error means the body was not valid JSON or its structure didn't match the struct. Usually the server returned an HTML anti-bot/login page or an error page instead of the API payload.","triggerScenarios":"Response body is HTML (WAF challenge, Cloudflare interstitial, login redirect), body is empty, or the API changed its JSON schema so unmarshal fails (e.g. data no longer an object).","commonSituations":"ensureSession cookies expired or the challenge failed so the site serves a JS challenge page; site redesign changed the response envelope; a captive portal or ISP interception page is returned.","solutions":["Log the first bytes of body on unmarshal failure to see what was actually returned","Re-run/refresh the anti-bot session (ensureSession) before retrying the search","Update NSGameResponse struct fields to match the current API schema","Check whether nsthwj.cn moved to a new API domain and update apiURL","Validate body starts with '{' before unmarshalling to give a clearer error"],"exampleFix":"// before\nvar apiResp NSGameResponse\nif err := json.Unmarshal(body, &apiResp); err != nil {\n    return nil, fmt.Errorf(\"[%s] JSON解析失败: %w\", p.Name(), err)\n}\n// after\nvar apiResp NSGameResponse\nif err := json.Unmarshal(body, &apiResp); err != nil {\n    return nil, fmt.Errorf(\"[%s] JSON解析失败(响应开头: %.120q): %w\", p.Name(), body, err)\n}","handlingStrategy":"fallback","validationCode":"// hint: validate content type before trusting the body\nif !strings.Contains(resp.Header.Get(\"Content-Type\"), \"application/json\") {\n    log.Warn(\"expected JSON but got\", \"ct\", resp.Header.Get(\"Content-Type\"))\n}","typeGuard":null,"tryCatchPattern":"results, err := plugin.Search(keyword, ext)\nif err != nil && strings.Contains(err.Error(), \"JSON解析失败\") {\n    // likely anti-bot HTML: refresh session once, else fall back to other plugins\n    return otherPlugins.Search(keyword, ext)\n}","preventionTips":["Keep the anti-bot session (ensureSession) fresh before searching","Check Content-Type header before parsing JSON","Log response prefixes on parse failure to catch schema changes fast","Pin/verify the API response shape with a small contract test"],"tags":["json","parsing","go","unexpected-response"],"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"}