{"record":{"id":"46cc4ba95a4f5de3","repo":"evanw/esbuild","slug":"must-specify-both-key-and-certificate-for-https","errorCode":null,"errorMessage":"Must specify both key and certificate for HTTPS","messagePattern":"Must specify both key and certificate for HTTPS","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/api/serve_other.go","lineNumber":763,"sourceCode":"}\n\nfunc (ctx *internalContext) Serve(serveOptions ServeOptions) (ServeResult, error) {\n\tctx.mutex.Lock()\n\tdefer ctx.mutex.Unlock()\n\n\t// Ignore disposed contexts\n\tif ctx.didDispose {\n\t\treturn ServeResult{}, errors.New(\"Cannot serve a disposed context\")\n\t}\n\n\t// Don't allow starting serve mode multiple times\n\tif ctx.handler != nil {\n\t\treturn ServeResult{}, errors.New(\"Serve mode has already been enabled\")\n\t}\n\n\t// Don't allow starting serve mode multiple times\n\tif (serveOptions.Keyfile != \"\") != (serveOptions.Certfile != \"\") {\n\t\treturn ServeResult{}, errors.New(\"Must specify both key and certificate for HTTPS\")\n\t}\n\n\t// Validate the \"servedir\" path\n\tif serveOptions.Servedir != \"\" {\n\t\tif absPath, ok := ctx.realFS.Abs(serveOptions.Servedir); ok {\n\t\t\tserveOptions.Servedir = absPath\n\t\t} else {\n\t\t\treturn ServeResult{}, fmt.Errorf(\"Invalid serve path: %s\", serveOptions.Servedir)\n\t\t}\n\t}\n\n\t// Validate the \"fallback\" path\n\tif serveOptions.Fallback != \"\" {\n\t\tif absPath, ok := ctx.realFS.Abs(serveOptions.Fallback); ok {\n\t\t\tserveOptions.Fallback = absPath\n\t\t} else {\n\t\t\treturn ServeResult{}, fmt.Errorf(\"Invalid fallback path: %s\", serveOptions.Fallback)\n\t\t}","sourceCodeStart":745,"sourceCodeEnd":781,"githubUrl":"https://github.com/evanw/esbuild/blob/f6058f8364fe7ab91ca57a83e02577ed74c9cae4/pkg/api/serve_other.go#L745-L781","documentation":"For HTTPS serve mode, esbuild requires both a key file and a certificate file. This error fires when exactly one of Keyfile or Certfile is set (checked via an XOR comparison: (Keyfile != \"\") != (Certfile != \"\")). Both must be provided together, or neither for plain HTTP.","triggerScenarios":"Passing --keyfile without --certfile (or vice versa) on the CLI, or setting keyfile but not certfile in the serve options.","commonSituations":"Partially configuring TLS by providing the private key but forgetting the certificate chain, or vice versa, when setting up a dev server.","solutions":["Provide both keyfile and certfile paths together","If you do not need HTTPS, omit both keyfile and certfile entirely"],"exampleFix":"// before\nesbuild.serve({ servedir: '.', keyfile: 'key.pem' })\n// after\nesbuild.serve({ servedir: '.', keyfile: 'key.pem', certfile: 'cert.pem' })","handlingStrategy":"validation","validationCode":"function validateServeOptions(opts) {\n  const hasKey = !!opts.keyfile\n  const hasCert = !!opts.certfile\n  if (hasKey !== hasCert) {\n    throw new Error('Both keyfile and certfile must be provided for HTTPS, or neither for HTTP')\n  }\n}\nvalidateServeOptions(serveOptions)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Provide both keyfile and certfile as a pair, or omit both for plain HTTP","Generate both key and certificate together using tools like mkcert or openssl","Add a config validation step before calling serve"],"tags":["esbuild","serve","https","tls","config"],"backgroundTag":null,"analyzedSha":"f6058f8364fe7ab91ca57a83e02577ed74c9cae4","analyzedAt":"2026-08-09T18:37:22.223Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}