{"record":{"id":"59608c3eb3d245cb","repo":"crowdsecurity/crowdsec","slug":"url-is-required","errorCode":null,"errorMessage":"url is required","messagePattern":"url is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/longpollclient/client.go","lineNumber":265,"sourceCode":"\t\tc.logger.Tracef(\"got response: %+v\", pollResp)\n\n\t\tif pollResp.ErrorMessage != \"\" {\n\t\t\tif pollResp.ErrorMessage == timeoutMessage {\n\t\t\t\tc.logger.Debugf(\"got timeout message\")\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tlog.Errorf(\"longpoll API error message: %s\", pollResp.ErrorMessage)\n\t\t\tbreak\n\t\t}\n\t\tevts = append(evts, pollResp.Events...)\n\t}\n\treturn evts, nil\n}\n\nfunc NewLongPollClient(config LongPollClientConfig) (*LongPollClient, error) {\n\tvar logger *log.Entry\n\tif config.Url == (url.URL{}) {\n\t\treturn nil, errors.New(\"url is required\")\n\t}\n\tif config.Logger == nil {\n\t\tlogger = log.WithField(\"component\", \"longpollclient\")\n\t} else {\n\t\tlogger = config.Logger.WithFields(log.Fields{\n\t\t\t\"component\": \"longpollclient\",\n\t\t\t\"url\":       config.Url.String(),\n\t\t})\n\t}\n\n\treturn &LongPollClient{\n\t\turl:        config.Url,\n\t\tlogger:     logger,\n\t\thttpClient: config.HttpClient,\n\t}, nil\n}\n","sourceCodeStart":247,"sourceCodeEnd":282,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/longpollclient/client.go#L247-L282","documentation":"Constructor validation in NewLongPollClient. A LongPollClientConfig must carry a non-zero target url.URL; without it the client would long-poll an empty endpoint, so construction fails immediately. Called via NewPAPI when wiring up the PAPI client.","triggerScenarios":"Calling NewLongPollClient (directly or through NewPAPI) with LongPollClientConfig{} or a config whose Url field was never populated (url.URL zero value).","commonSituations":"Programmatic use of the longpollclient package with a partially filled config struct, or an upstream setup function that fails to load the PAPI endpoint from configuration before constructing the client.","solutions":["Set the Url field, e.g. url.Parse(\"https://papi.api.crowdsec.net\") before calling the constructor.","Check NewPAPI's caller path to ensure configuration loading (profiles/API URL) succeeded and was passed through.","Guard construction: parse and validate the URL, skip/abort the PAPI client if absent."],"exampleFix":"// before\ncfg := LongPollClientConfig{Logger: logger}\nclient, _ := NewLongPollClient(cfg)\n// after\nu, err := url.Parse(\"https://papi.api.crowdsec.net\")\nif err != nil { return err }\ncfg := LongPollClientConfig{Logger: logger, Url: *u}\nclient, err := NewLongPollClient(cfg)","handlingStrategy":"validation","validationCode":"if cfg.Url == (url.URL{}) {\n    return errors.New(\"longpollclient config requires Url\")\n}\nif _, err := url.Parse(cfg.Url.String()); err != nil {\n    return fmt.Errorf(\"invalid longpoll url: %w\", err)\n}","typeGuard":"func hasURL(c LongPollClientConfig) bool { return c.Url != url.URL{} }","tryCatchPattern":"client, err := NewLongPollClient(cfg)\nif err != nil {\n    return fmt.Errorf(\"PAPI client init failed: %w\", err)\n}","preventionTips":["Always construct config with a parsed URL before NewLongPollClient","Centralize PAPI endpoint configuration in one loader function","Add a unit test asserting the constructor rejects zero-value configs"],"tags":["validation","constructor","papi"],"backgroundTag":"missing-required-argument","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}