denoland/deno · error · TypeError

If `key` is specified, `cert` must be specified as well for

Error message

If `key` is specified, `cert` must be specified as well for `${api}`

What it means

The mirror-image check in loadTlsKeyPair(): providing key without cert throws this TypeError. Both halves of the key pair are mandatory whenever certificate-based TLS is configured, for clients (connectTls) and servers (listenTls) alike.

Source

Thrown at ext/net/02_tls.js:153

  // TODO(mmastrac): remove this temporary symbol when the API lands
  if (arguments[1][resolverSymbol] !== undefined) {
    return createTlsKeyResolver(arguments[1][resolverSymbol]);
  }

  // Check for "pem" format
  if (keyFormat !== undefined && keyFormat !== "pem") {
    throw new TypeError(
      `If "keyFormat" is specified, it must be "pem": received "${keyFormat}"`,
    );
  }

  if (cert !== undefined && key === undefined) {
    throw new TypeError(
      `If \`cert\` is specified, \`key\` must be specified as well for \`${api}\``,
    );
  }
  if (cert === undefined && key !== undefined) {
    throw new TypeError(
      `If \`key\` is specified, \`cert\` must be specified as well for \`${api}\``,
    );
  }

  if (cert !== undefined) {
    return op_tls_key_static(cert, key);
  } else {
    return op_tls_key_null();
  }
}

function listenTls({
  port = 0,
  hostname = "0.0.0.0",
  transport = "tcp",
  alpnProtocols = undefined,
  reusePort = false,
  tcpBacklog = 511,

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Provide both cert and key as PEM strings
  2. Validate the pair at startup: if either cert or key is missing, abort with a clear configuration error
  3. Check that both secret files/env vars resolve before constructing options

Example fix

// before
Deno.listenTls({ port: 443, key: Deno.readTextFileSync("key.pem") });

// after
const [cert, key] = await Promise.all([
  Deno.readTextFile("cert.pem"),
  Deno.readTextFile("key.pem"),
]);
Deno.listenTls({ port: 443, cert, key });
Defensive patterns

Strategy: validation

Validate before calling

function requireKeyPair(opts: { cert?: string; key?: string }): { cert: string; key: string } {
  const { cert, key } = opts;
  if ((cert !== undefined || key !== undefined) && (cert === undefined || key === undefined)) {
    throw new Error("TLS options require both cert and key together");
  }
  return { cert: cert!, key: key! };
}

Type guard

function hasCompleteKeyPair(o: { cert?: string; key?: string }): o is { cert: string; key: string } {
  return (o.cert === undefined) === (o.key === undefined);
}

Prevention

When it happens

Trigger: Deno.connectTls({ key }) / Deno.listenTls({ key }) with no cert; options built from env where only TLS_KEY was set.

Common situations: Secrets mounted individually (key present, cert missing); refactors that moved cert into another variable; half-configured mTLS client setups.

Related errors


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