{"record":{"id":"77a279930f91ef57","repo":"fish2018/pansou","slug":"s-w-77a279","errorCode":null,"errorMessage":"[%s] 创建请求失败: %w","messagePattern":"\\[(.+?)\\] 创建请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/ash/ash.go","lineNumber":84,"sourceCode":"\n// SearchWithResult 执行搜索并返回包含IsFinal标记的结果\nfunc (p *AshPlugin) SearchWithResult(keyword string, ext map[string]interface{}) (model.PluginSearchResult, error) {\n\treturn p.AsyncSearchWithResult(keyword, p.searchImpl, p.MainCacheKey, ext)\n}\n\n// searchImpl 实际的搜索实现（优化版本）\nfunc (p *AshPlugin) searchImpl(client *http.Client, keyword string, ext map[string]interface{}) ([]model.SearchResult, error) {\n\t// 构建搜索URL\n\tsearchURL := fmt.Sprintf(\"https://so.allsharehub.com/s/%s.html\", url.QueryEscape(keyword))\n\t\n\t// 创建带超时的上下文（减少超时时间，提高响应速度）\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\t\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\t\n\t// 设置请求头\n\tp.setRequestHeaders(req)\n\t\n\t// 发送请求（优化重试）\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\n\t// 检查状态码\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"[%s] 请求返回状态码: %d\", p.Name(), resp.StatusCode)\n\t}\n\t\n\t// 读取响应（使用有限制的读取，避免读取过大内容）","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/ash/ash.go#L66-L102","documentation":"In ash's searchImpl, http.NewRequestWithContext fails while building the GET request for the search URL. The plugin wraps the error with its plugin name so the caller knows which plugin failed. This almost always means the search URL string is malformed (unparseable scheme/host) rather than a network problem.","triggerScenarios":"http.NewRequestWithContext(ctx, \"GET\", searchURL, nil) returns an error because searchURL cannot be parsed by url.Parse (invalid characters, bad scheme, control characters) — ash.go:84.","commonSituations":"Base URL constant edited to include a typo, spaces or full-width characters; config value interpolated into the URL containing unescaped characters; empty or corrupted base URL from a bad config merge.","solutions":["Log/inspect the searchURL string right before NewRequestWithContext and validate it with url.Parse.","Fix the base URL constant or configuration that supplies the host/path.","Escape or trim user-controlled parts of the URL (url.PathEscape / url.QueryEscape) before building it.","Reject empty/whitespace URLs early with an explicit validation error."],"exampleFix":"// before\nreq, err := http.NewRequestWithContext(ctx, \"GET\", searchURL, nil)\n// after\nu, perr := url.Parse(searchURL)\nif perr != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return nil, fmt.Errorf(\"[%s] 无效的搜索URL: %q\", p.Name(), searchURL)\n}\nreq, err := http.NewRequestWithContext(ctx, \"GET\", u.String(), nil)","handlingStrategy":"validation","validationCode":"u, err := url.Parse(searchURL); if err != nil || u.Scheme == \"\" || u.Host == \"\" { return fmt.Errorf(\"invalid search URL: %q\", searchURL) }","typeGuard":null,"tryCatchPattern":"if err != nil {\n    var uerr *url.Error\n    if errors.As(err, &uerr) {\n        log.Printf(\"bad URL %q: %v\", uerr.URL, uerr.Err)\n    }\n    return err\n}","preventionTips":["Validate config-supplied base URLs at startup","Use url.PathEscape/QueryEscape for all interpolated values","Reject empty/whitespace URLs early","Add a unit test constructing the request with representative keywords"],"tags":["http","url","request-construction","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"}