{"record":{"id":"ead58786f3467945","repo":"fish2018/pansou","slug":"w","errorCode":null,"errorMessage":"创建请求失败: %w","messagePattern":"创建请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/bixin/bixin.go","lineNumber":185,"sourceCode":"\t\n\t// 按时间降序排序\n\tsort.Slice(unique, func(i, j int) bool {\n\t\treturn unique[i].Datetime.After(unique[j].Datetime)\n\t})\n\t\n\treturn unique\n}\n\n// fetchPage 获取指定页的搜索结果\nfunc (p *BixinAsyncPlugin) fetchPage(client *http.Client, keyword string, offset int) ([]model.SearchResult, bool, error) {\n\t// 构建API URL\n\tapiURL := fmt.Sprintf(\"%s?filter[q]=%s&include=mostRelevantPost&page[offset]=%d&page[limit]=%d\",\n\t\tBaseURL, url.QueryEscape(keyword), offset, PageSize)\n\t\n\t// 创建请求\n\treq, err := http.NewRequest(\"GET\", apiURL, nil)\n\tif err != nil {\n\t\treturn nil, false, fmt.Errorf(\"创建请求失败: %w\", err)\n\t}\n\t\n\t// 设置请求头\n\treq.Header.Set(\"User-Agent\", getRandomUA())\n\treq.Header.Set(\"X-Forwarded-For\", generateRandomIP())\n\treq.Header.Set(\"Accept\", \"application/json, text/plain, */*\")\n\treq.Header.Set(\"Accept-Language\", \"zh-CN,zh;q=0.9,en;q=0.8\")\n\treq.Header.Set(\"Connection\", \"keep-alive\")\n\treq.Header.Set(\"Sec-Fetch-Dest\", \"empty\")\n\treq.Header.Set(\"Sec-Fetch-Mode\", \"cors\")\n\treq.Header.Set(\"Sec-Fetch-Site\", \"same-origin\")\n\t\n\tvar resp *http.Response\n\tvar responseBody []byte\n\t\n\t// 重试逻辑\n\tfor i := 0; i <= p.retries; i++ {\n\t\t// 发送请求","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/bixin/bixin.go#L167-L203","documentation":"bixin's fetchPage builds the search API URL (BaseURL + filter/include/page query params) and calls http.NewRequest(\"GET\", apiURL, nil). If the URL cannot be parsed, it returns (nil, false, '创建请求失败: %w'). Note this call has no context deadline, unlike the ash plugin, so failures here are construction-time only.","triggerScenarios":"http.NewRequest returns an error because apiURL — built with url.QueryEscape(keyword) interpolated into BaseURL — is malformed: BaseURL empty or containing bad scheme/characters, or keyword escaping produced an invalid URL string — bixin.go:185.","commonSituations":"BaseURL constant mis-edited or overridden by an empty config value; keyword containing characters that break URL structure after naive interpolation; leading/trailing whitespace in a configured host.","solutions":["Log apiURL on failure and validate with url.Parse before calling NewRequest.","Fix the BaseURL constant/config supplying the endpoint host.","Validate keyword (non-empty, no control chars) and rely on url.QueryEscape (already used) for interpolation.","Add a context deadline via http.NewRequestWithContext so slow requests fail predictably too."],"exampleFix":"// before\nreq, err := http.NewRequest(\"GET\", apiURL, nil)\n// after\nu, perr := url.Parse(apiURL)\nif perr != nil || u.Host == \"\" {\n    return nil, false, fmt.Errorf(\"创建请求失败: 无效URL %q: %w\", apiURL, perr)\n}\nreq, err := http.NewRequestWithContext(context.Background(), \"GET\", u.String(), nil)","handlingStrategy":"validation","validationCode":"u, err := url.Parse(apiURL); if err != nil || u.Scheme == \"\" || u.Host == \"\" { return fmt.Errorf(\"invalid api URL: %q\", apiURL) }","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var uerr *url.Error\n    if errors.As(err, &uerr) { log.Printf(\"bad URL: %q: %v\", uerr.URL, uerr.Err) }\n    return nil, false, err\n}","preventionTips":["Validate BaseURL at plugin init, not per request","Keep using url.QueryEscape for keyword interpolation","Trim configured hosts of whitespace","Switch to http.NewRequestWithContext with a deadline"],"tags":["http","url","request-construction","go"],"backgroundTag":"invalid-url","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"}