{"record":{"id":"bf7545f050ac8f89","repo":"fatedier/frp","slug":"tls-keyfile-must-be-specified-when-tls-is-enabled","errorCode":null,"errorMessage":"tls.keyFile must be specified when tls is enabled","messagePattern":"tls\\.keyFile must be specified when tls is enabled","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/config/v1/validation/common.go","lineNumber":30,"sourceCode":"// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage validation\n\nimport (\n\t\"fmt\"\n\t\"slices\"\n\n\tv1 \"github.com/fatedier/frp/pkg/config/v1\"\n)\n\nfunc validateWebServerConfig(c *v1.WebServerConfig) error {\n\tif c.TLS != nil {\n\t\tif c.TLS.CertFile == \"\" {\n\t\t\treturn fmt.Errorf(\"tls.certFile must be specified when tls is enabled\")\n\t\t}\n\t\tif c.TLS.KeyFile == \"\" {\n\t\t\treturn fmt.Errorf(\"tls.keyFile must be specified when tls is enabled\")\n\t\t}\n\t}\n\n\treturn ValidatePort(c.Port, \"webServer.port\")\n}\n\n// ValidatePort checks that the network port is in range\nfunc ValidatePort(port int, fieldPath string) error {\n\tif 0 <= port && port <= 65535 {\n\t\treturn nil\n\t}\n\treturn fmt.Errorf(\"%s: port number %d must be in the range 0..65535\", fieldPath, port)\n}\n\nfunc validateLogConfig(c *v1.LogConfig) error {\n\tif !slices.Contains(SupportedLogLevels, c.Level) {\n\t\treturn fmt.Errorf(\"invalid log level, optional values are %v\", SupportedLogLevels)\n\t}","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/config/v1/validation/common.go#L12-L48","documentation":"Companion to the certFile check: when webServer.tls is enabled, tls.keyFile must also be non-empty. A certificate without its private key cannot complete the TLS handshake for the admin dashboard.","triggerScenarios":"webServer.tls present with certFile set but keyFile empty/omitted; asymmetric config after switching from a combined PEM to separate files.","commonSituations":"Cert/key managed by different tools (cert renewed, key path not updated); template that fills certFile from a variable but leaves keyFile blank on some hosts.","solutions":["Set webServer.tls.keyFile to the matching private key path","Ensure the key matches the certificate (frp will fail at startup if not)","If TLS is not intended, drop the whole webServer.tls block"],"exampleFix":"# before\n[webServer.tls]\ncertFile = \"admin.crt\"\n\n# after\n[webServer.tls]\ncertFile = \"admin.crt\"\nkeyFile = \"admin.key\"","handlingStrategy":"validation","validationCode":"func webServerTLSComplete(c *v1.WebServerConfig) bool {\n    return c.TLS == nil || (c.TLS.CertFile != \"\" && c.TLS.KeyFile != \"\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Manage cert+key as one unit; update both on renewal","Check readability of both files in deployment smoke tests"],"tags":["frp","config","webserver","tls","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}