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
Deno.listenTls throws this TypeError when the options contain no key-pair material at all (hasTlsKeyPairOptions is false and no key resolver was registered). A TLS server must present a certificate, unlike Deno.connectTls where cert/key are optional client credentials. The check runs before the listening socket is created.
Solutions
- Generate or obtain a PEM cert/key pair and pass both strings
- For local dev create a self-signed pair: openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes
- Load the files with await Deno.readTextFile before calling listenTls, and verify they are non-empty strings
Example fix
// before
const listener = Deno.listenTls({ port: 443 }); // no cert/key
// after
const cert = await Deno.readTextFile('cert.pem');
const key = await Deno.readTextFile('key.pem');
const listener = Deno.listenTls({ port: 443, cert, key }); Defensive patterns
Strategy: validation
Validate before calling
const cert = await Deno.readTextFile('cert.pem').catch(() => undefined);
const key = await Deno.readTextFile('key.pem').catch(() => undefined);
if (!cert?.includes('BEGIN') || !key?.includes('BEGIN')) {
throw new Error('listenTls needs PEM cert and key; generate with openssl req -x509 -newkey rsa:2048');
} Type guard
function hasTlsKeyPairOptions(o: Record<string, unknown>): boolean {
return typeof (o as { cert?: unknown }).cert === 'string' &&
typeof (o as { key?: unknown }).key === 'string';
} Try / catch
try {
Deno.listenTls({ port, cert, key });
} catch (e) {
if (e instanceof TypeError && e.message.includes('key and certificate are required')) {
throw new Error(`TLS cert/key missing or empty (cert=${!!cert}, key=${!!key})`);
}
throw e;
} Prevention
- Run a startup preflight that loads and sanity-checks both PEM files before binding
- Distinguish client and server roles: servers always need their own certificate
- Include certificate renewal checks in ops tooling so cert/key never silently disappear
When it happens
Trigger: Deno.listenTls({ port: 443 }); Deno.listenTls({ port: 443, keyFormat: 'pem' }) (a format alone is not a key pair); cert/key read asynchronously and passed as undefined; Node-style option names like pfx/passphrase that the API does not read.
Common situations: Assuming listenTls works like connectTls with no certificate; pointing at Let's Encrypt files with wrong paths so readTextFile never ran; local dev servers started before certificates were generated.
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
- If "keyFormat" is specified, it must be "pem": received
- Unsupported transport
- Unsupported transport
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/bf15b302c491a85a.
Report an issue: GitHub.
Appendix: 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 9ad36f7a2c)