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

  1. 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'
  2. Pass both as PEM strings: cert: await Deno.readTextFile('cert.pem'), key: await Deno.readTextFile('key.pem')
  3. 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

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

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/bf15b302c491a85a. Report an issue: GitHub.