{"record":{"id":"04e6019e9db0cfc2","repo":"fish2018/pansou","slug":"s-w-04e601","errorCode":null,"errorMessage":"[%s] 创建搜索请求失败: %w","messagePattern":"\\[(.+?)\\] 创建搜索请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/dygang/dygang.go","lineNumber":175,"sourceCode":"\t\t\t\tmu.Unlock()\n\t\t\t}\n\t\t}()\n\t}\n\twg.Wait()\n\treturn plugin.FilterResultsByKeyword(results, keyword), nil\n}\n\nfunc (p *Plugin) fetchSearch(client *http.Client, keyword string) (*goquery.Document, error) {\n\tencoded, err := encodeGB18030(keyword)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 编码搜索关键词失败: %w\", p.Name(), err)\n\t}\n\tform := \"tempid=1&tbname=article&keyboard=\" + url.QueryEscape(string(encoded)) + \"&show=title%2Csmalltext&Submit=\"\n\tctx, cancel := context.WithTimeout(context.Background(), requestTimeout)\n\tdefer cancel()\n\treq, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+searchPath, strings.NewReader(form))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 创建搜索请求失败: %w\", p.Name(), err)\n\t}\n\tsetHeaders(req, baseURL+\"/\")\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求失败: %w\", p.Name(), err)\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"[%s] 搜索请求返回 HTTP %d\", p.Name(), resp.StatusCode)\n\t}\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, 4<<20))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 读取搜索结果失败: %w\", p.Name(), err)\n\t}\n\tdecoded, err := decodeGB18030(body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] 解码搜索结果失败: %w\", p.Name(), err)","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/dygang/dygang.go#L157-L193","documentation":"fetchSearch wraps any error returned by http.NewRequestWithContext with this message, prefixed by the plugin name. The request construction itself failed before any network I/O occurred — most commonly because baseURL+searchPath does not parse as a valid URL. This is a local construction error, not a network problem.","triggerScenarios":"http.NewRequestWithContext(ctx, http.MethodPost, baseURL+searchPath, strings.NewReader(form)) returns an error — i.e. the combined URL fails url.Parse (malformed scheme/host, control characters or spaces in baseURL, empty baseURL producing a non-absolute URL).","commonSituations":"A misconfigured baseURL in the plugin config (missing scheme like 'http://', trailing garbage, or an empty value), or a baseURL containing whitespace/newlines read from an env var or config file without trimming.","solutions":["Verify the plugin's configured baseURL starts with a valid scheme and host (e.g. https://example.com) and has no stray spaces; trim it before use.","Pre-validate the URL with url.Parse on baseURL+searchPath and fail fast with a clear config error.","Check that searchPath is a well-formed constant path and no config value is being concatenated into it."],"exampleFix":"// before\nbaseURL := cfg[\"baseURL\"] // may be \" example.com\"\nreq, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+searchPath, strings.NewReader(form))\n// after\nbaseURL := strings.TrimSpace(cfg[\"baseURL\"])\nif u, perr := url.Parse(baseURL); perr != nil || u.Scheme == \"\" || u.Host == \"\" {\n\treturn nil, fmt.Errorf(\"invalid baseURL %q\", baseURL)\n}\nreq, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+searchPath, strings.NewReader(form))","handlingStrategy":"validation","validationCode":"u, err := url.Parse(baseURL + searchPath)\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n\treturn fmt.Errorf(\"invalid search URL: %q\", baseURL+searchPath)\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n\treturn nil, fmt.Errorf(\"[%s] 创建搜索请求失败: %w\", p.Name(), err)\n}","preventionTips":["Always configure baseURL with scheme and host, trimmed of whitespace","Validate config URLs at plugin startup, not per request","Keep searchPath as a tested constant"],"tags":["http","url","request-construction","config"],"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"}