{"record":{"id":"5fd2351588978a04","repo":"fish2018/pansou","slug":"s-d-w-5fd235","errorCode":null,"errorMessage":"[%s] 创建第%d页请求失败: %w","messagePattern":"\\[(.+?)\\] 创建第(.+?)页请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/xiaoyu/xiaoyu.go","lineNumber":152,"sourceCode":"\t\t\tif _, exists := seen[key]; exists {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tseen[key] = struct{}{}\n\t\t\tresults = append(results, result)\n\t\t}\n\t}\n\n\treturn plugin.FilterResultsByKeyword(results, keyword), nil\n}\n\nfunc (p *XiaoyuPlugin) fetchPage(client *http.Client, keyword string, page int) (pageResult, error) {\n\trequestURL := fmt.Sprintf(searchURL, page, url.PathEscape(keyword))\n\tctx, cancel := context.WithTimeout(context.Background(), requestTimeout)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)\n\tif err != nil {\n\t\treturn pageResult{}, fmt.Errorf(\"[%s] 创建第%d页请求失败: %w\", p.Name(), page, err)\n\t}\n\tsetRequestHeaders(req)\n\n\tresp, err := doRequestWithRetry(client, req)\n\tif err != nil {\n\t\treturn pageResult{}, fmt.Errorf(\"[%s] 第%d页搜索请求失败: %w\", p.Name(), page, err)\n\t}\n\tdefer resp.Body.Close()\n\n\tdoc, err := goquery.NewDocumentFromReader(io.LimitReader(resp.Body, maxResponseSize))\n\tif err != nil {\n\t\treturn pageResult{}, fmt.Errorf(\"[%s] 第%d页解析失败: %w\", p.Name(), page, err)\n\t}\n\n\treturn parsePage(doc), nil\n}\n\nfunc setRequestHeaders(req *http.Request) {","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/xiaoyu/xiaoyu.go#L134-L170","documentation":"fetchPage builds the per-page search GET request with http.NewRequestWithContext. If request construction fails (almost always an invalid URL or unencodable parameters), the error is wrapped with the plugin name and page number. This fires before any network I/O occurs.","triggerScenarios":"http.NewRequestWithContext returns an error for the URL built by fmt.Sprintf(searchURL, page, url.PathEscape(keyword)) — typically because keyword contains characters that produce a malformed URL, or searchURL template is misconfigured.","commonSituations":"Keyword containing control characters or an invalid escape sequence; searchURL constant edited incorrectly (bad %s placeholders or missing scheme); page number formatting producing an invalid path segment.","solutions":["Print requestURL on failure and inspect for missing scheme or illegal characters.","Sanitize/validate the keyword before interpolation (strip control characters, re-run url.PathEscape).","Check the searchURL format string still matches the current site URL scheme.","Since NewRequestWithContext rarely fails for valid URLs, log the raw keyword that triggered it."],"exampleFix":"// before\nrequestURL := fmt.Sprintf(searchURL, page, url.PathEscape(keyword))\n// after\nrequestURL := fmt.Sprintf(searchURL, page, url.PathEscape(strings.TrimSpace(keyword)))\nif _, err := url.Parse(requestURL); err != nil {\n\treturn pageResult{}, fmt.Errorf(\"invalid search url %q: %w\", requestURL, err)\n}","handlingStrategy":"validation","validationCode":"func validKeyword(k string) bool {\n\tfor _, r := range k {\n\t\tif r < 0x20 || r == 0x7f { return false }\n\t}\n\treturn strings.TrimSpace(k) != \"\"\n}","typeGuard":"func isPrintableKeyword(s string) bool { return utf8.ValidString(s) && strings.TrimSpaceFunc(s, func(r rune) bool { return !unicode.IsPrint(r) }) == \"\" }","tryCatchPattern":"req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)\nif err != nil {\n\treturn pageResult{}, fmt.Errorf(\"[%s] page %d bad url %q: %w\", p.Name(), page, requestURL, err)\n}","preventionTips":["Validate/sanitize keywords (trim, strip control chars) before building the URL.","Sanity-check the assembled URL with url.Parse before constructing the request.","Keep the searchURL format string under test so edits cannot silently break it."],"tags":["http-request","url","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"}