evanw/esbuild · error
Must specify both key and certificate for HTTPS
Error message
Must specify both key and certificate for HTTPS
What it means
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.
Solutions
- Provide both keyfile and certfile paths together
- If you do not need HTTPS, omit both keyfile and certfile entirely
Example fix
// before
esbuild.serve({ servedir: '.', keyfile: 'key.pem' })
// after
esbuild.serve({ servedir: '.', keyfile: 'key.pem', certfile: 'cert.pem' }) Defensive patterns
Strategy: validation
Validate before calling
function validateServeOptions(opts) {
const hasKey = !!opts.keyfile
const hasCert = !!opts.certfile
if (hasKey !== hasCert) {
throw new Error('Both keyfile and certfile must be provided for HTTPS, or neither for HTTP')
}
}
validateServeOptions(serveOptions) Prevention
- 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
When it happens
Trigger: Passing --keyfile without --certfile (or vice versa) on the CLI, or setting keyfile but not certfile in the serve options.
Common situations: Partially configuring TLS by providing the private key but forgetting the certificate chain, or vice versa, when setting up a dev server.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Cannot serve without an output path
- Invalid origin
- Invalid port number
- Output directory must be contained in serve directory
- Cannot compute relative path from
AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09).
Data as JSON: /api/errors/46cc4ba95a4f5de3.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/serve_other.go:763
}
func (ctx *internalContext) Serve(serveOptions ServeOptions) (ServeResult, error) {
ctx.mutex.Lock()
defer ctx.mutex.Unlock()
// Ignore disposed contexts
if ctx.didDispose {
return ServeResult{}, errors.New("Cannot serve a disposed context")
}
// Don't allow starting serve mode multiple times
if ctx.handler != nil {
return ServeResult{}, errors.New("Serve mode has already been enabled")
}
// Don't allow starting serve mode multiple times
if (serveOptions.Keyfile != "") != (serveOptions.Certfile != "") {
return ServeResult{}, errors.New("Must specify both key and certificate for HTTPS")
}
// Validate the "servedir" path
if serveOptions.Servedir != "" {
if absPath, ok := ctx.realFS.Abs(serveOptions.Servedir); ok {
serveOptions.Servedir = absPath
} else {
return ServeResult{}, fmt.Errorf("Invalid serve path: %s", serveOptions.Servedir)
}
}
// Validate the "fallback" path
if serveOptions.Fallback != "" {
if absPath, ok := ctx.realFS.Abs(serveOptions.Fallback); ok {
serveOptions.Fallback = absPath
} else {
return ServeResult{}, fmt.Errorf("Invalid fallback path: %s", serveOptions.Fallback)
}View on GitHub (pinned to f6058f8364)