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
- Provide both cert and key as PEM strings
- Validate the pair at startup: if either cert or key is missing, abort with a clear configuration error
- 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
- Symmetric secret naming (MYAPP_TLS_CERT / MYAPP_TLS_KEY) checked together
- Never build TLS options through partial spreads that can drop one field
- Validate the pair before constructing the options object
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
- If `cert` is specified, `key` must be specified as well for
- If "keyFormat" is specified, it must be "pem": received "${k
- A key and certificate are required for `Deno.listenTls`
- Unsupported transport: '${transport}'
- Invalid port: '${maybePort}'
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/9e13741383ed0e5a.
Report an issue: GitHub.