{"record":{"id":"bbb29f6fa306efae","repo":"crowdsecurity/crowdsec","slug":"missing-tls-cert-file-bbb29f","errorCode":null,"errorMessage":"missing TLS cert file","messagePattern":"missing TLS cert file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pkg/apiserver/apiserver.go","lineNumber":382,"sourceCode":"// listenAndServeLAPI starts the http server and blocks until it's closed\n// it also updates the URL field with the actual address the server is listening on\n// it's meant to be run in a separate goroutine\nfunc (s *APIServer) listenAndServeLAPI(ctx context.Context, apiReady chan bool) error {\n\tserverError := make(chan error, 2)\n\n\tlistenConfig := &net.ListenConfig{}\n\n\tstartServer := func(listener net.Listener, canTLS bool) {\n\t\tvar err error\n\n\t\tif canTLS && s.cfg.TLS != nil && (s.cfg.TLS.CertFilePath != \"\" || s.cfg.TLS.KeyFilePath != \"\") {\n\t\t\tif s.cfg.TLS.KeyFilePath == \"\" {\n\t\t\t\tserverError <- errors.New(\"missing TLS key file\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif s.cfg.TLS.CertFilePath == \"\" {\n\t\t\t\tserverError <- errors.New(\"missing TLS cert file\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\terr = s.httpServer.ServeTLS(listener, s.cfg.TLS.CertFilePath, s.cfg.TLS.KeyFilePath)\n\t\t} else {\n\t\t\terr = s.httpServer.Serve(listener)\n\t\t}\n\n\t\tswitch {\n\t\tcase errors.Is(err, http.ErrServerClosed):\n\t\t\tbreak\n\t\tcase err != nil:\n\t\t\tserverError <- err\n\t\t}\n\t}\n\n\t// Starting TCP listener\n\tgo func(url string) {","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/apiserver/apiserver.go#L364-L400","documentation":"Mirror of the key-file check: TLS is enabled because a key file path is configured, but cert_file_path is empty. ServeTLS needs both a certificate and a key, so the server aborts with 'missing TLS cert file'.","triggerScenarios":"api.server.tls configured with key_file_path set but cert_file_path empty or omitted from the YAML.","commonSituations":"Partial TLS setup where the key was generated but the cert (or CA-signed cert) was never deployed or the path was mistyped/templated empty; config migration losing one of the two fields.","solutions":["Set api.server.tls.cert_file_path to the PEM certificate path.","Confirm the cert file exists and is readable by the crowdsec process.","If TLS is not intended, remove the key_file_path too so both fields are empty and the server serves plain HTTP."],"exampleFix":"# before\napi:\n  server:\n    tls:\n      key_file_path: /etc/ssl/private/lapi.key\n# after\napi:\n  server:\n    tls:\n      cert_file_path: /etc/ssl/certs/lapi.crt\n      key_file_path: /etc/ssl/private/lapi.key","handlingStrategy":"validation","validationCode":"tls := cfg.API.Server.TLS\nif tls != nil && (tls.CertFilePath != \"\" || tls.KeyFilePath != \"\") {\n    if tls.CertFilePath == \"\" {\n        return errors.New(\"tls.cert_file_path is required when TLS is enabled\")\n    }\n    if _, err := os.Stat(tls.CertFilePath); err != nil { return err }\n}","typeGuard":null,"tryCatchPattern":"if err := server.StartApiserver(ctx); err != nil {\n    log.Fatalf(\"LAPI startup failed: %v\", err)\n}","preventionTips":["Deploy cert and key together; never set one TLS path without the other.","Run config validation in CI before shipping crowdsec configs.","Verify file paths exist and are readable post-deploy."],"tags":["tls","lapi","configuration"],"backgroundTag":"missing-required-config-field","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}