{"record":{"id":"a7bec1a1b92022c4","repo":"fish2018/pansou","slug":"s-suggest-w","errorCode":null,"errorMessage":"[%s] suggest 响应解析失败: %w","messagePattern":"\\[(.+?)\\] suggest 响应解析失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/qiwei/qiwei.go","lineNumber":210,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif isVerifyPage(body) {\n\t\tif err := p.solveVerification(client, searchURL, body); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"[%s] 搜索验证失败: %w\", p.Name(), err)\n\t\t}\n\t\tbody, err = p.fetchBody(client, searchURL, host+\"/\", searchTimeout)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif isVerifyPage(body) {\n\t\t\treturn nil, fmt.Errorf(\"[%s] 命中验证页: %s\", p.Name(), host)\n\t\t}\n\t}\n\n\tvar resp suggestResponse\n\tif err := json.Unmarshal([]byte(body), &resp); err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] suggest 响应解析失败: %w\", p.Name(), err)\n\t}\n\n\tif resp.Code != 1 && len(resp.List) == 0 {\n\t\treturn nil, fmt.Errorf(\"[%s] suggest 响应异常: host=%s code=%d msg=%s\", p.Name(), host, resp.Code, resp.Msg)\n\t}\n\n\treturn resp.List, nil\n}\n\nfunc (p *QiweiPlugin) enrichResults(client *http.Client, host string, items []suggestItem, forceRefresh bool) []model.SearchResult {\n\tresults := make([]model.SearchResult, len(items))\n\tvar wg sync.WaitGroup\n\tsem := make(chan struct{}, maxConcurrent)\n\n\tfor i, item := range items {\n\t\twg.Add(1)\n\t\tgo func(idx int, it suggestItem) {\n\t\t\tdefer wg.Done()","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/qiwei/qiwei.go#L192-L228","documentation":"Raised in searchSuggest (plugin/qiwei/qiwei.go:210) when the suggest endpoint's response body cannot be parsed as the expected suggestResponse JSON (code/msg/list). The json unmarshal error is wrapped with %w. Typically the site returned HTML (error page, verification page not caught by isVerifyPage, CDN block page) instead of the AJAX JSON payload.","triggerScenarios":"json.Unmarshal([]byte(body), &resp) returns an error after a successful fetchBody of /index.php/ajax/suggest?mid=1&limit=100&wd=<keyword> — i.e. HTTP succeeded but the body is not the expected JSON object.","commonSituations":"Site is behind a CDN serving an HTML challenge/error page that isVerifyPage's huadong regex doesn't recognize; keyword edge cases hitting a non-AJAX fallback page; the site changed its suggest API response format; server returned an HTML 5xx error page with status 200.","solutions":["Log/print the first ~200 bytes of body on this error to see what was actually returned (HTML vs JSON) — that determines the fix.","If it's an unrecognized block/verification page, extend isVerifyPage's regexes in qiwei.go to detect it so the verification flow runs instead.","If the API response shape changed, update the suggestResponse struct fields/tags.","Retry with a different keyword or later; transient HTML error pages clear once upstream recovers."],"exampleFix":"// before (debugging aid inside searchSuggest)\nif err := json.Unmarshal([]byte(body), &resp); err != nil {\n    return nil, fmt.Errorf(\"[%s] suggest 响应解析失败: %w\", p.Name(), err)\n}\n// after\nif err := json.Unmarshal([]byte(body), &resp); err != nil {\n    return nil, fmt.Errorf(\"[%s] suggest 响应解析失败: %w, body=%q\", p.Name(), err, body[:min(200, len(body))])\n}","handlingStrategy":"validation","validationCode":"// validate the body looks like the expected JSON before unmarshalling\ntrimmed := strings.TrimSpace(body)\nif !strings.HasPrefix(trimmed, \"{\") {\n    return fmt.Errorf(\"non-JSON suggest response (likely HTML): %.100s\", trimmed)\n}","typeGuard":null,"tryCatchPattern":"var resp suggestResponse\nif err := json.Unmarshal([]byte(body), &resp); err != nil {\n    log.Printf(\"qiwei suggest returned non-JSON body: %.200s\", body)\n    return nil, fmt.Errorf(\"suggest parse failed: %w\", err) // host loop will try next mirror\n}","preventionTips":["Log response bodies on parse failure to distinguish HTML block pages from format changes","Keep isVerifyPage regexes up to date so HTML challenges route to the verification flow, not the JSON parser","Pin keyword encoding via url.QueryEscape (already done) to avoid malformed request URLs"],"tags":["json","parsing","scraping","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"}