microsoft/playwright · error · Error

key is specified without cert

Error message

key is specified without cert

What it means

Thrown by verifyClientCertificates() when a clientCertificates entry has key set but cert is falsy. A private key without its matching certificate cannot be used for TLS client auth, so Playwright rejects the entry before constructing a secure context.

Source

Thrown at packages/playwright-core/src/server/browserContext.ts:806

    throw new Error(`geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed.`);
  if (latitude < -90 || latitude > 90)
    throw new Error(`geolocation.latitude: precondition -90 <= LATITUDE <= 90 failed.`);
  if (accuracy < 0)
    throw new Error(`geolocation.accuracy: precondition 0 <= ACCURACY failed.`);
}

export function verifyClientCertificates(clientCertificates?: types.BrowserContextOptions['clientCertificates']) {
  if (!clientCertificates)
    return;
  for (const cert of clientCertificates) {
    if (!cert.origin)
      throw new Error(`clientCertificates.origin is required`);
    if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx)
      throw new Error('None of cert, key, passphrase or pfx is specified');
    if (cert.cert && !cert.key)
      throw new Error('cert is specified without key');
    if (!cert.cert && cert.key)
      throw new Error('key is specified without cert');
    if (cert.pfx && (cert.cert || cert.key))
      throw new Error('pfx is specified together with cert, key or passphrase');
  }
}

export function normalizeProxySettings(proxy: types.ProxySettings): types.ProxySettings {
  let { server, bypass } = proxy;
  let url;
  try {
    // new URL('127.0.0.1:8080') throws
    // new URL('localhost:8080') fails to parse host or protocol
    // In both of these cases, we need to try re-parse URL with `http://` prefix.
    url = new URL(server);
    if (!url.host || !url.protocol)
      url = new URL('http://' + server);
  } catch (e) {
    url = new URL('http://' + server);
  }

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Provide cert alongside key (cert: fs.readFileSync('client.crt') or a valid certPath).
  2. If only a PKCS12 bundle is available, switch to pfx and remove the key field.
  3. Verify certPath is an existing, readable file so its Buffer is populated before validation.

Example fix

// before
clientCertificates: [{ origin, key: fs.readFileSync('client.key') }]
// after
clientCertificates: [{ origin, cert: fs.readFileSync('client.crt'), key: fs.readFileSync('client.key') }]
Defensive patterns

Strategy: validation

Validate before calling

function validateCertKeyPair(certs?: ClientCertificate[]) {
  for (const c of certs || []) {
    if (!c.cert && c.key) throw new Error(`key set without cert for ${c.origin}`);
  }
}

Type guard

function isCompleteCertKeyPair(c: ClientCertificate): boolean {
  return (!!c.cert && !!c.key) || (!c.cert && !c.key);
}

Prevention

When it happens

Trigger: browser.newContext({ clientCertificates: [{ origin, key: <Buffer> /* cert missing */ }] }), or the same via APIRequestContext.newContext / launchPersistentContext.

Common situations: Developer loads the .key file but forgets the .crt, or certPath resolves to undefined because the file does not exist.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/75e0c5064ff19a0e. Report an issue: GitHub.