evanw/esbuild · error
Must specify both key and certificate for HTTPS
Error message
Must specify both key and certificate for HTTPS
What it means
Returned by internalContext.Serve when exactly one of Keyfile / Certfile is set but not the other. HTTPS requires both a private key and a certificate; setting only one is treated as a misconfiguration and rejected before any listener is created. The XOR check is (Keyfile != "") != (Certfile != "").
Source
Thrown at pkg/api/serve_other.go:746
}
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 6ff1d8b0d8)
Solutions
- Provide both Keyfile and Certfile, or neither (for plain HTTP).
- Validate that both TLS file env vars are present before calling Serve().
- Check for typos: the option names are Keyfile and Certfile (note: key then cert).
- Log the resolved key/cert paths right before Serve() to catch empty values.
Example fix
// before
ctx.Serve({ port: 443, Keyfile: './key.pem' }); // Certfile missing
// after
ctx.Serve({ port: 443, Keyfile: './key.pem', Certfile: './cert.pem' }); Defensive patterns
Strategy: validation
Validate before calling
function validTLS(opts) { return (opts.Keyfile ? 1 : 0) + (opts.Certfile ? 1 : 0) !== 1; }
if (!validTLS(serveOpts)) throw new Error('Provide both key and cert, or neither'); Type guard
function hasBothTLSFiles(opts) { return !!opts.Keyfile && !!opts.Certfile; } Try / catch
try { await ctx.Serve(opts); } catch (e) { if (/Must specify both key and certificate/.test(e.message)) { /* supply the missing file */ } throw e; } Prevention
- Always set both Keyfile and Certfile together.
- Read both from env and fail fast if either is missing.
- Double-check option names (Keyfile, Certfile).
- Log resolved TLS paths before Serve().
When it happens
Trigger: Calling ctx.Serve() with ServeOptions where Keyfile is set but Certfile is empty, or vice versa. The XOR guard at serve_other.go:745 returns the error.
Common situations: A config loader that reads TLS_KEY from env but TLS_CERT is missing; typo in option names; copying a config where one path was left as the placeholder; a conditional that sets the keyfile only under some flag.
Related errors
- Invalid serve path: %s
- Invalid fallback path: %s
- Invalid origin: %s
- Cannot serve %s without an output path
- Cannot compute relative path from %q to %q
AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03).
Data as JSON: /data/errors/46cc4ba95a4f5de3.json.
Report an issue: GitHub.