{"record":{"id":"15d83b5d59d478e9","repo":"fish2018/pansou","slug":"s-w-15d83b","errorCode":null,"errorMessage":"[%s] 创建请求失败: %w","messagePattern":"\\[(.+?)\\] 创建请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/dy4k/dy4k.go","lineNumber":357,"sourceCode":"\n\t// 1. 构建搜索URL\n\tvar searchURL string\n\tif page == 1 {\n\t\tsearchURL = fmt.Sprintf(SearchURL, encodedKeyword)\n\t} else {\n\t\tsearchURL = fmt.Sprintf(SearchPageURL, encodedKeyword, page)\n\t}\n\n\tdebugPrintf(\"🔧 [Dy4k DEBUG] 构建的URL: %s\\n\", searchURL)\n\n\t// 2. 创建带超时的上下文\n\tctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)\n\tdefer cancel()\n\n\t// 3. 创建请求\n\treq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n\tif err != nil {\n\t\treturn nil, 0, fmt.Errorf(\"[%s] 创建请求失败: %w\", p.Name(), err)\n\t}\n\n\t// 4. 设置完整的请求头（包含随机UA和IP）\n\trandomUA := getRandomUA()\n\trandomIP := generateRandomIP()\n\n\treq.Header.Set(\"User-Agent\", randomUA)\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\", BaseURL+\"/\")\n\treq.Header.Set(\"X-Forwarded-For\", randomIP)\n\treq.Header.Set(\"X-Real-IP\", randomIP)\n\treq.Header.Set(\"sec-ch-ua-platform\", \"macOS\")\n\n\tdebugPrintf(\"🔧 [Dy4k DEBUG] 使用随机UA: %s\\n\", randomUA)","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/dy4k/dy4k.go#L339-L375","documentation":"dy4k's searchPage wraps a failure from http.NewRequestWithContext when constructing the GET request for one search results page. As with the duoduo plugin, this fires only when the page URL is unparseable or the context is invalid — before any network traffic. The plugin name is included for multi-source disambiguation.","triggerScenarios":"The per-page searchURL built from the base URL, keyword, and page number is malformed (unescaped CJK keyword characters, spaces, or empty base URL), making http.NewRequestWithContext return an error.","commonSituations":"Keyword contains characters that were not url.QueryEscape'd; configuration changed the base URL to something invalid; page number formatting produced a URL with an unexpected token; empty keyword yields a path-only URL that trips parsing in edge cases.","solutions":["Log searchURL before creating the request and inspect it for unescaped characters.","Escape the keyword with url.QueryEscape when building the URL.","Validate the composed URL with url.Parse before http.NewRequestWithContext.","Confirm the configured base URL for dy4k is correct and reachable."],"exampleFix":"// before\nsearchURL := fmt.Sprintf(\"%s/search/%s/%d\", base, keyword, page)\nreq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n// after\nsearchURL := fmt.Sprintf(\"%s/search/%s/%d\", base, url.QueryEscape(keyword), page)\nif _, perr := url.Parse(searchURL); perr != nil {\n    return nil, 0, fmt.Errorf(\"[%s] 无效搜索URL: %w\", p.Name(), perr)\n}\nreq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)","handlingStrategy":"validation","validationCode":"func buildSearchURL(base, keyword string, page int) (string, error) {\n    u := fmt.Sprintf(\"%s/search/%s/%d\", base, url.QueryEscape(keyword), page)\n    parsed, err := url.Parse(u)\n    if err != nil { return \"\", err }\n    if parsed.Scheme == \"\" || parsed.Host == \"\" { return \"\", fmt.Errorf(\"invalid url: %s\", u) }\n    return u, nil\n}","typeGuard":"func validRequestURL(s string) bool {\n    u, err := url.Parse(s)\n    return err == nil && (u.Scheme == \"http\" || u.Scheme == \"https\") && u.Host != \"\"\n}","tryCatchPattern":null,"preventionTips":["Escape every dynamic URL segment.","Compose URLs via url.Values / url.URL rather than Sprintf on raw strings.","Validate base URL configuration at startup.","Unit-test URL construction with CJK and special-character keywords."],"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"}