{"record":{"id":"c72e5c6a050dceaf","repo":"fish2018/pansou","slug":"s-w-c72e5c","errorCode":null,"errorMessage":"[%s] 创建请求失败: %w","messagePattern":"\\[(.+?)\\] 创建请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/clmao/clmao.go","lineNumber":179,"sourceCode":"}\n\n// searchPage 搜索指定页面\nfunc (p *ClmaoPlugin) searchPage(client *http.Client, keyword string, page int) ([]model.SearchResult, error) {\n\t// clm64 encodes the keyword as base64 and uses query-style pagination.\n\tencodedKeyword := base64.StdEncoding.EncodeToString([]byte(keyword))\n\tsearchURL := fmt.Sprintf(\"%s/search?word=%s&sort=time\", BaseURL, url.QueryEscape(encodedKeyword))\n\tif page > 1 {\n\t\tsearchURL += fmt.Sprintf(\"&p=%d\", page)\n\t}\n\n\t// 创建带超时的上下文\n\tctx, cancel := context.WithTimeout(context.Background(), TimeoutSeconds*time.Second)\n\tdefer cancel()\n\n\t// 创建请求\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\n\t// 设置请求头\n\tp.setRequestHeaders(req)\n\n\t// 发送HTTP请求\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\n\t// 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\n\t// 读取响应体内容","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/clmao/clmao.go#L161-L197","documentation":"ClmaoPlugin.searchPage fails while constructing the outbound http.Request via http.NewRequestWithContext before any network I/O happens. This wraps a Go standard-library error such as a malformed URL (net/url.Parse error), invalid method, or a bad body passed to NewRequest. The request context with TimeoutSeconds is created just before, so context creation itself is not the cause.","triggerScenarios":"searchPage builds searchURL (derived from the keyword) and calls http.NewRequestWithContext(ctx, \"GET\", searchURL, nil). If searchURL is empty, contains characters invalid for a URL, or the keyword was not escaped, url.Parse fails and the error is wrapped as \"[clmao] 创建请求失败: %w\".","commonSituations":"Keyword containing spaces, CJK characters, or special characters (&, #, %) that were not url.QueryEscape'd into searchURL; a configuration change broke the base URL constant; programmatic construction of the URL from an empty/unvalidated keyword.","solutions":["Print/inspect the wrapped error — it names the exact URL parse problem.","Ensure the keyword is escaped before interpolation: url.QueryEscape(keyword) or use url.Values{\"q\": {keyword}}.Encode().","Validate searchURL with url.Parse and log it (sanitized) before creating the request.","Fix the base URL constant if the site changed domains or paths."],"exampleFix":"// before\nsearchURL := fmt.Sprintf(\"https://example.com/search?q=%s\", keyword)\nreq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n// after\nsearchURL := fmt.Sprintf(\"https://example.com/search?q=%s\", url.QueryEscape(keyword))\nif _, perr := url.Parse(searchURL); perr != nil {\n    return nil, fmt.Errorf(\"invalid search url: %w\", perr)\n}\nreq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)","handlingStrategy":"validation","validationCode":"func validSearchURL(base string, keyword string) (string, error) {\n    if strings.TrimSpace(keyword) == \"\" { return \"\", errors.New(\"empty keyword\") }\n    u := fmt.Sprintf(\"%s?q=%s\", base, url.QueryEscape(keyword))\n    if _, err := url.Parse(u); err != nil { return \"\", fmt.Errorf(\"invalid url: %w\", err) }\n    return u, nil\n}","typeGuard":null,"tryCatchPattern":"results, err := p.searchPage(client, keyword, page)\nif err != nil {\n    if strings.Contains(err.Error(), \"创建请求失败\") {\n        // URL construction problem: fix keyword escaping/base URL, not retryable\n        return nil, fmt.Errorf(\"bad request construction: %w\", err)\n    }\n    return nil, err\n}","preventionTips":["Always url.QueryEscape user-supplied keywords before building URLs.","Validate the final URL with url.Parse before issuing the request.","Keep the base URL in configuration and verify it when the site migrates domains.","Reject empty keywords early."],"tags":["http","url","request-construction","scraping"],"backgroundTag":"invalid-url-format","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"}