denoland/deno · error · TypeError
A key and certificate are required for `Deno.listenTls`
Error message
A key and certificate are required for `Deno.listenTls`
What it means
Unlike Deno.connectTls (where cert/key are optional client certificates), a TLS server must present a certificate. listenTls checks hasTlsKeyPairOptions() on the original arguments and throws this TypeError when neither cert nor key is present - a TLS listener cannot start keyless.
Source
Thrown at ext/net/02_tls.js:179
return op_tls_key_null();
}
}
function listenTls({
port = 0,
hostname = "0.0.0.0",
transport = "tcp",
alpnProtocols = undefined,
reusePort = false,
tcpBacklog = 511,
}) {
if (transport !== "tcp") {
throw new TypeError(`Unsupported transport: '${transport}'`);
}
port = validatePort(port, true);
if (!hasTlsKeyPairOptions(arguments[0])) {
throw new TypeError(
"A key and certificate are required for `Deno.listenTls`",
);
}
const keyPair = loadTlsKeyPair("Deno.listenTls", arguments[0]);
const { 0: rid, 1: localAddr } = op_net_listen_tls(
{ hostname, port },
{ alpnProtocols, reusePort, tcpBacklog },
keyPair,
);
localAddr.transport = transport;
return new TlsListener(rid, localAddr);
}
// deno-lint-ignore require-await
async function startTls(
conn,
{
hostname = "127.0.0.1",View on GitHub (pinned to 89f33cbef2)
Solutions
- Generate a self-signed pair for development: openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'
- Pass both as PEM strings: cert: await Deno.readTextFile('cert.pem'), key: await Deno.readTextFile('key.pem')
- In production use real certificates (e.g. Let's Encrypt) injected via secrets/env at startup
Example fix
// before
const listener = Deno.listenTls({ port: 443 }); // TypeError
// after
const listener = Deno.listenTls({
port: 443,
cert: await Deno.readTextFile("cert.pem"),
key: await Deno.readTextFile("key.pem"),
}); Defensive patterns
Strategy: validation
Validate before calling
async function loadTlsOptions(): Promise<{ cert: string; key: string }> {
const certPath = Deno.env.get("TLS_CERT_PATH");
const keyPath = Deno.env.get("TLS_KEY_PATH");
if (!certPath || !keyPath) {
throw new Error(
"listenTls requires a certificate and key - set TLS_CERT_PATH and TLS_KEY_PATH (generate a self-signed pair for dev: openssl req -x509 -newkey rsa:2048 -nodes ...)",
);
}
return { cert: await Deno.readTextFile(certPath), key: await Deno.readTextFile(keyPath) };
} Type guard
function hasTlsCertAndKey(o: { cert?: string; key?: string }): o is { cert: string; key: string } {
return typeof o.cert === "string" && typeof o.key === "string" && o.cert !== "" && o.key !== "";
} Prevention
- Generate a committed-for-dev self-signed pair and document the openssl command
- Fail fast at startup when TLS paths are unset instead of calling listenTls
- Distinguish server usage (cert/key mandatory) from client usage (optional mTLS) in your config schema
When it happens
Trigger: Deno.listenTls({ port: 443 }) with no cert/key; passing them under wrong property names; nesting them inside another object so destructuring misses them.
Common situations: Local prototypes assuming the runtime auto-generates a self-signed certificate (browsers' dev-cert behavior); migrating from dev servers that auto-provision certs; CI where secret env vars are not yet injected.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- If `cert` is specified, `key` must be specified as well for
- If `key` is specified, `cert` must be specified as well for
- Unsupported transport: '${transport}'
- If "keyFormat" is specified, it must be "pem": received "${k
- ERR_TLS_REQUIRED_SERVER_NAME
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/bf15b302c491a85a.
Report an issue: GitHub.