{"record":{"id":"57df58a6d846a694","repo":"fish2018/pansou","slug":"s-w-57df58","errorCode":null,"errorMessage":"[%s] 创建请求失败: %w","messagePattern":"\\[(.+?)\\] 创建请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/duoduo/duoduo.go","lineNumber":164,"sourceCode":"\t\tatomic.AddInt64(&totalSearchTime, duration)\n\t}()\n\n\t// 使用优化的客户端\n\tif p.optimizedClient != nil {\n\t\tclient = p.optimizedClient\n\t}\n\n\t// 1. 构建搜索URL\n\tsearchURL := fmt.Sprintf(\"https://tv.yydsys.top/index.php/vod/search/wd/%s.html\", url.QueryEscape(keyword))\n\t\n\t// 2. 创建带超时的上下文\n\tctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)\n\tdefer cancel()\n\t\n\t// 3. 创建请求\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建请求失败: %w\", p.Name(), err)\n\t}\n\t\n\t// 4. 设置完整的请求头（避免反爬虫）\n\treq.Header.Set(\"User-Agent\", \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36\")\n\treq.Header.Set(\"Accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\")\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(\"Upgrade-Insecure-Requests\", \"1\")\n\treq.Header.Set(\"Cache-Control\", \"max-age=0\")\n\treq.Header.Set(\"Referer\", \"https://tv.yydsys.top/\")\n\t\n\t// 5. 发送请求（带重试机制）\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\t","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/duoduo/duoduo.go#L146-L182","documentation":"This error wraps a failure from http.NewRequestWithContext while building the GET request to the duoduo search URL inside searchImpl. In practice this only fails when the URL string cannot be parsed or the context is invalid, since no network I/O happens here. The plugin wraps the underlying err so the plugin name and root cause are preserved in the message.","triggerScenarios":"searchURL passed to http.NewRequestWithContext is malformed (e.g. contains spaces, unescaped characters, or is empty because the configured base URL is wrong or the search keyword was not URL-encoded).","commonSituations":"Base URL misconfigured or site domain changed and someone hand-builds a URL with raw CJK/keyword characters; keyword not passed through url.QueryEscape; empty search term producing a truncated URL.","solutions":["Print/log the exact searchURL string before http.NewRequestWithContext and fix malformed characters.","Wrap dynamic parts with url.QueryEscape: searchURL := baseURL + \"?q=\" + url.QueryEscape(keyword).","Use url.Parse on the constructed URL to validate it before issuing the request.","Verify the configured site base URL is current and correct."],"exampleFix":"// before\nsearchURL := fmt.Sprintf(\"%s/vodsearch/-------------.html?wd=%s\", base, keyword)\nreq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n// after\nsearchURL := fmt.Sprintf(\"%s/vodsearch/-------------.html?wd=%s\", base, url.QueryEscape(keyword))\nif _, perr := url.Parse(searchURL); perr != nil { return nil, perr }\nreq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)","handlingStrategy":"validation","validationCode":"func validateSearchURL(base, keyword string) error {\n    if base == \"\" { return fmt.Errorf(\"base URL 为空\") }\n    u := base + \"?wd=\" + url.QueryEscape(keyword)\n    _, err := url.Parse(u)\n    return err\n}","typeGuard":"func isValidURL(s string) bool {\n    u, err := url.Parse(s)\n    return err == nil && u.Scheme != \"\" && u.Host != \"\"\n}","tryCatchPattern":null,"preventionTips":["Always url.QueryEscape user-supplied keywords.","Validate composed URLs with url.Parse before issuing requests.","Keep base URLs in configuration, not hardcoded strings.","Add a startup self-test that builds a sample search URL."],"tags":["http","url-parsing","go","request-construction"],"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"}