{"record":{"id":"e710ace242d1cad0","repo":"router-for-me/CLIProxyAPI","slug":"create-request-w-e710ac","errorCode":null,"errorMessage":"create request: %w","messagePattern":"create request: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/pluginstore/github.go","lineNumber":212,"sourceCode":"\t\treturn c.HTTPClient\n\t}\n\treturn http.DefaultClient\n}\n\nfunc (c Client) userAgent() string {\n\tif strings.TrimSpace(c.UserAgent) != \"\" {\n\t\treturn strings.TrimSpace(c.UserAgent)\n\t}\n\treturn userAgent\n}\n\nfunc pluginStoreGetNoRedirect(ctx context.Context, client HTTPDoer, requestURL string, headers http.Header) (*http.Response, error) {\n\tif client == nil {\n\t\tclient = http.DefaultClient\n\t}\n\treq, errRequest := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil)\n\tif errRequest != nil {\n\t\treturn nil, fmt.Errorf(\"create request: %w\", errRequest)\n\t}\n\treq.Header = headers.Clone()\n\tresp, errDo := pluginStoreNoRedirectClient(client).Do(req)\n\tif errDo != nil {\n\t\treturn nil, pluginStoreRequestError(requestURL, errDo)\n\t}\n\treturn resp, nil\n}\n\nfunc pluginStoreNoRedirectClient(client HTTPDoer) HTTPDoer {\n\thttpClient, ok := client.(*http.Client)\n\tif !ok {\n\t\treturn client\n\t}\n\tclone := *httpClient\n\tclone.CheckRedirect = func(*http.Request, []*http.Request) error {\n\t\treturn http.ErrUseLastResponse\n\t}","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginstore/github.go#L194-L230","documentation":"pluginStoreGetNoRedirect builds the http.Request for each hop of the manual redirect follower. 'create request: %w' wraps the error from http.NewRequestWithContext and indicates the request could not even be constructed — the URL string (or context) is invalid at the net/http level, before any network I/O.","triggerScenarios":"A request URL that fails parsing/validation in net/http: control characters, spaces, or a completely malformed URL reaching this function — most plausibly a Location header from a previous redirect that could not be normalized, or an empty/garbage URL passed into the getter.","commonSituations":"A redirect Location containing raw spaces or CR/LF (some misconfigured servers emit these); a caller-supplied base URL assembled by string concatenation with an unescaped component. Note pluginStoreRedirectURL already resolves and validates Location, so reaching this with a bad URL usually means the initial URL was broken.","solutions":["Log the exact URL being fetched when this error appears; it will contain the malformed component.","url.PathEscape or properly join URL components when constructing release/asset URLs instead of raw fmt.Sprintf of user input.","If a server emits malformed Location headers, pin the artifact URL to a well-behaved host."],"exampleFix":"// before\nu := fmt.Sprintf(\"https://host/%s\", plugin.Name) // name with spaces -> create request error\n\n// after\nu := fmt.Sprintf(\"https://host/%s\", url.PathEscape(plugin.Name))","handlingStrategy":"validation","validationCode":"u, err := url.Parse(rawURL)\nif err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return fmt.Errorf(\"invalid plugin store url %q\", rawURL)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always url.PathEscape dynamic path segments.","Parse-and-validate URLs at config load time rather than at request time."],"tags":["http","url-validation","request-construction","plugin-store"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}