denoland/deno · error · TypeError

If "keyFormat" is specified, it must be "pem": received

Error message

If "keyFormat" is specified, it must be "pem": received "${keyFormat}"

What it means

Deno's TLS key-pair loader (loadTlsKeyPair, used by Deno.connectTls and Deno.listenTls) accepts a keyFormat option, but only the PEM format is implemented: any value other than 'pem' (undefined also means PEM) throws this TypeError. DER and PKCS#12 key material are not supported.

Solutions

  1. Convert key and certificate to PEM (base64 bodies with BEGIN/END headers) and omit keyFormat
  2. If specifying the field, use exactly the lowercase string 'pem'
  3. Generate PEM directly: openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes

Example fix

# before: DER material + keyFormat: 'der'
openssl rsa -in key.der -inform DER -out key.der  # still DER

# after: convert to PEM and drop keyFormat
openssl rsa -in key.der -inform DER -out key.pem -outform PEM
openssl x509 -in cert.der -inform DER -out cert.pem -outform PEM
Defensive patterns

Strategy: validation

Validate before calling

if (keyFormat !== undefined && keyFormat !== 'pem') {
  throw new Error(`only PEM keys are supported; convert ${keyFormat} to PEM before calling`);
}

Type guard

function isPemFormat(f: unknown): f is 'pem' | undefined {
  return f === undefined || f === 'pem';
}

Try / catch

try {
  await Deno.connectTls({ hostname, port, cert, key, keyFormat });
} catch (e) {
  if (e instanceof TypeError && e.message.includes('keyFormat')) {
    throw new Error('convert key/cert to PEM and drop keyFormat');
  }
  throw e;
}

Prevention

When it happens

Trigger: Deno.connectTls({ cert, key, keyFormat: 'der' }); values translated from Node's secure-context options such as 'pkcs8' or 'p12'; config files supplying uppercase 'PEM' (the check is case-sensitive lowercase 'pem').

Common situations: Converting Node tls.createSecureContext options to Deno; keys generated via openssl DER output formats; configuration systems that always populate format fields with a default like 'PEM'.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/476df96f9ba72659. Report an issue: GitHub.

Appendix: source

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

}

/**
 * Loads a TLS keypair from one of the various options. If no key material is provided,
 * returns a special Null keypair.
 */
function loadTlsKeyPair(api, {
  keyFormat,
  cert,
  key,
}) {
  // 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 {

View on GitHub (pinned to 9ad36f7a2c)