{"record":{"id":"9f46f3b14119501c","repo":"fish2018/pansou","slug":"s-s-d-w","errorCode":null,"errorMessage":"[%s] %s网盘第%d页创建请求失败: %w","messagePattern":"\\[(.+?)\\] (.+?)网盘第(.+?)页创建请求失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/haisou/haisou.go","lineNumber":328,"sourceCode":"\n// fetchSearchPage 获取指定网盘类型的单页搜索结果\nfunc (p *HaisouPlugin) fetchSearchPage(client *http.Client, keyword string, pageNo int, panType string) ([]ShareItem, error) {\n\t// 构建搜索URL\n\tsearchURL := fmt.Sprintf(\"https://haisou.cc/api/pan/share/search?query=%s&scope=title&pan=%s&page=%d&filter_valid=true&filter_has_files=false\",\n\t\turl.QueryEscape(keyword), panType, pageNo)\n\n\tif DebugLog {\n\t\tfmt.Printf(\"[%s] 请求%s网盘第%d页: %s\\n\", p.Name(), panType, pageNo, searchURL)\n\t}\n\n\t// 创建带超时的上下文\n\tctx, cancel := context.WithTimeout(context.Background(), 30*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] %s网盘第%d页创建请求失败: %w\", p.Name(), panType, pageNo, err)\n\t}\n\n\t// 设置请求头\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\", \"application/json, text/plain, */*\")\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(\"Referer\", \"https://haisou.cc/\")\n\n\t// 发送HTTP请求（带重试机制）\n\tresp, err := p.doRequestWithRetry(req, client)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] %s网盘第%d页请求失败: %w\", p.Name(), panType, pageNo, err)\n\t}\n\tdefer resp.Body.Close()\n\n\t// 检查状态码\n\tif resp.StatusCode != 200 {","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/fish2018/pansou/blob/beaa56133755a548ebc51b090b3816e2ae044aa6/plugin/haisou/haisou.go#L310-L346","documentation":"fetchSearchPage builds an http.NewRequestWithContext GET to the haisou search API and wraps any construction error with this message naming the plugin, pan type, and page number. This fires before any network traffic — it means the request object itself could not be created, almost always because the search URL was malformed (e.g. empty panType/keyword producing an invalid URL).","triggerScenarios":"Calling fetchSearchPage when http.NewRequestWithContext fails: invalid URL string from a bad panType/keyword/pageNo combination, unescaped control characters in the query, or an empty base URL.","commonSituations":"Query keywords containing characters not URL-encoded; site config with a wrong/empty base URL; a pan type constant typo producing a bad path; regression after URL template changes.","solutions":["Log/inspect the constructed searchURL; pass it through url.Parse to see why it is invalid.","URL-escape the keyword with url.QueryEscape before inserting into the URL template.","Fix the panType/pageNo values being passed in (must be non-empty, positive).","Verify the site base URL configured in the plugin is correct."],"exampleFix":"// before\nsearchURL := fmt.Sprintf(\"%s/api/search?kw=%s&page=%d\", base, keyword, pageNo)\n// after\nsearchURL := fmt.Sprintf(\"%s/api/search?kw=%s&page=%d\", base, url.QueryEscape(keyword), pageNo)","handlingStrategy":"validation","validationCode":"if panType == \"\" || keyword == \"\" || pageNo < 1 {\n    return errors.New(\"panType, keyword and pageNo must be valid before building the request\")\n}\nsearchURL := fmt.Sprintf(\"%s/api/search?kw=%s&page=%d\", base, url.QueryEscape(keyword), pageNo)\nif _, err := url.Parse(searchURL); err != nil {\n    return fmt.Errorf(\"invalid search URL %q: %w\", searchURL, err)\n}","typeGuard":null,"tryCatchPattern":"items, err := fetchSearchPage(panType, keyword, pageNo)\nvar urlErr *url.Error\nif errors.As(err, &urlErr) {\n    return fmt.Errorf(\"bad request URL: %w\", err)\n}","preventionTips":["Always url.QueryEscape user-supplied keywords before building URLs.","Validate panType against the known set of constants.","Sanity-check the configured base URL at plugin startup.","Keep pageNo bounded and non-negative."],"tags":["http","url","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"}