denoland/deno · error · TypeError
Both 'cert' and 'key' must be provided to enable HTTPS
Error message
Both 'cert' and 'key' must be provided to enable HTTPS
What it means
When serve options request HTTPS (signaled by cert/key presence), serveInner requires both halves of the keypair: a certificate alone or a private key alone cannot build a TLS listener. This TypeError fires synchronously at startup when exactly one of `options.cert` / `options.key` is set, before any socket is bound.
Solutions
- Provide both `cert` and `key` PEM strings whenever either is present
- Validate before starting: `if (env.CERT || env.KEY) require both` and fail with a clear message
- Gate the whole TLS block on availability of both secrets, or default to plain HTTP in dev
Example fix
// before
Deno.serve({ port: 443, cert: pem, handler }); // key missing
// after
Deno.serve({
port: 443,
cert: await Deno.readText('./cert.pem'),
key: await Deno.readText('./key.pem'),
handler,
}); Defensive patterns
Strategy: validation
Validate before calling
// Require the keypair as a unit before starting:
const cert = env.TLS_CERT, key = env.TLS_KEY;
if ((cert ?? key) !== undefined && (!cert || !key)) {
throw new Error('HTTPS requires both TLS_CERT and TLS_KEY');
}
const opts = cert && key ? { cert, key, handler } : { handler };
Deno.serve(opts); Prevention
- Treat cert and key as one inseparable config unit; validate both before serve
- Fail startup with a clear message when only one TLS secret is present in the environment
- Default to plain HTTP in dev instead of half-configured TLS
When it happens
Trigger: `Deno.serve({ cert })` without key, `key` present but `cert` missing, or conditionally spreading TLS options (`...(tls ?? {})`) where only one field is defined — e.g. cert loaded from an env var whose key counterpart is unset.
Common situations: Environment-driven TLS config where one secret is missing in a deploy environment, copy-paste from examples that only showed one field, templating bugs that drop one PEM.
Related errors
- Unsupported 'certFile' / 'keyFile' options provided: use…
- Unsupported 'alpnProtocols' option provided. 'h2' and…
- Cannot serve HTTP requests: either a `handler` or `options`…
- Cannot serve HTTP requests: handler must be a function…
- ERR_INVALID_PROTOCOL
AI-assisted analysis of denoland/deno@abd22074e4 (2026-08-20).
Data as JSON: /api/errors/3b918da2ba8b93dc.
Report an issue: GitHub.
Appendix: source
Thrown at ext/http/00_serve.ts:1405
loadBalanced: options[kLoadBalanced] ?? false,
tcpBacklog: options.tcpBacklog,
};
if (options.certFile || options.keyFile) {
throw new TypeError(
"Unsupported 'certFile' / 'keyFile' options provided: use 'cert' / 'key' instead.",
);
}
if (options.alpnProtocols) {
throw new TypeError(
"Unsupported 'alpnProtocols' option provided. 'h2' and 'http/1.1' are automatically supported.",
);
}
let listener;
if (wantsHttps) {
if (!options.cert || !options.key) {
throw new TypeError(
"Both 'cert' and 'key' must be provided to enable HTTPS",
);
}
listenOpts.cert = options.cert;
listenOpts.key = options.key;
listenOpts.alpnProtocols = ["h2", "http/1.1"];
listener = listenTls(listenOpts);
listenOpts.port = listener.addr.port;
} else {
listener = listen(listenOpts);
listenOpts.port = listener.addr.port;
}
const addr = listener.addr;
const onListen = (scheme) => {
if (options.onListen) {
options.onListen(addr);View on GitHub (pinned to abd22074e4)