denoland/deno · error · TypeError
If `cert` is specified, `key` must be specified as well for
Error message
If `cert` is specified, `key` must be specified as well for `${api}` What it means
loadTlsKeyPair requires cert and key to be passed together; specifying cert without key throws this TypeError, with the message naming the offending API (Deno.connectTls or Deno.listenTls). A TLS identity is inherently a pair - a certificate alone cannot be used, so the check runs before any socket or TLS context is created.
Solutions
- Pass both strings: Deno.listenTls({ port, cert: await Deno.readTextFile('cert.pem'), key: await Deno.readTextFile('key.pem') })
- Verify both files/env vars exist before building the options object
- If you do not need a client certificate, omit cert as well - server-only verification needs neither
Example fix
// before
Deno.listenTls({ port: 443, cert }); // key missing
// 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 } {
if (opts.cert === undefined || opts.key === undefined) {
throw new Error('TLS identity requires both cert and key');
}
return { cert: opts.cert, key: opts.key };
} Type guard
function hasCompleteKeyPair(o: { cert?: string; key?: string }): o is { cert: string; key: string } {
return typeof o.cert === 'string' && typeof o.key === 'string' &&
o.cert.length > 0 && o.key.length > 0;
} Try / catch
try {
Deno.listenTls(opts);
} catch (e) {
if (e instanceof TypeError && e.message.includes('`key` must be specified')) {
throw new Error(`missing TLS key file (cert was loaded from ${certPath})`);
}
throw e;
} Prevention
- Load cert and key in one Promise.all so one missing file fails loudly and early
- Validate that both PEM strings are non-empty and contain BEGIN/END markers
- For client connections without client certificates, pass neither cert nor key
When it happens
Trigger: Deno.listenTls({ port: 443, cert: pem }) with no key; Deno.connectTls({ cert }) attempting mutual TLS with a missing client key; destructuring a config object where the key field was dropped (typo, undefined).
Common situations: Secrets split across files where only cert.pem got deployed; config objects assembled conditionally so key ends up undefined; examples copied from docs that omitted the key line.
Related errors
- If `key` is specified, `cert` must be specified as well for
- A key and certificate are required for `Deno.listenTls`
- If "keyFormat" is specified, it must be "pem": received
- Unsupported transport
- Dispatcher connect.ca must be a string, Buffer, or…
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/7c0bdf2360b383c4.
Report an issue: GitHub.
Appendix: source
Thrown at ext/net/02_tls.js:148
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 {
return op_tls_key_null();
}
}
function listenTls({
port = 0,View on GitHub (pinned to 9ad36f7a2c)