{"record":{"id":"251dc3a4ae5f263e","repo":"microsoft/playwright","slug":"clientcertificates-for-origin-origin-mix-nocertificate-with","errorCode":null,"errorMessage":"clientCertificates for origin \"${origin}\" mix noCertificate with a real certificate","messagePattern":"clientCertificates for origin \"(.+?)\" mix noCertificate with a real certificate","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts","lineNumber":337,"sourceCode":"    if (proxyFromEnv)\n      return createProxyAgent({ server: proxyFromEnv });\n  }\n\n  _initSecureContexts(clientCertificates: types.BrowserContextOptions['clientCertificates']) {\n    // Step 1. Group certificates by origin.\n    const origin2certs = new Map<string, NonNullable<types.BrowserContextOptions['clientCertificates']>>();\n    for (const cert of clientCertificates || []) {\n      const origin = normalizeOrigin(cert.origin);\n      const certs = origin2certs.get(origin) || [];\n      certs.push(cert);\n      origin2certs.set(origin, certs);\n    }\n\n    // Step 2. Create secure contexts for each origin.\n    for (const [origin, certs] of origin2certs) {\n      const noCertificateCount = certs.filter(cert => cert.noCertificate).length;\n      if (noCertificateCount > 0 && noCertificateCount < certs.length)\n        throw new Error(`clientCertificates for origin \"${origin}\" mix noCertificate with a real certificate`);\n      if (noCertificateCount > 0) {\n        this.secureContextMap.set(origin, undefined);\n        continue;\n      }\n      try {\n        this.secureContextMap.set(origin, tls.createSecureContext(convertClientCertificatesToTLSOptions(certs)));\n      } catch (error) {\n        error = rewriteOpenSSLErrorIfNeeded(error);\n        throw rewriteErrorMessage(error, `Failed to load client certificate: ${error.message}`);\n      }\n    }\n  }\n\n  public static async create(progress: Progress, contextOptions: Pick<types.BrowserContextOptions, 'clientCertificates' | 'ignoreHTTPSErrors' | 'proxy'>) {\n    const proxy = new ClientCertificatesProxy(contextOptions);\n    try {\n      await progress.race(proxy._socksProxy.listen(0, '127.0.0.1'));\n      return proxy;","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/microsoft/playwright/blob/500c9c822ce7664539a4c8a88810048dfe090c3b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts#L319-L355","documentation":"SocksClientCertificatesInterceptor builds one TLS secure context per origin. If some clientCertificates entries for an origin have noCertificate=true and others carry real certificate material, the interceptor cannot decide whether to require or bypass client auth for that origin and throws during construction.","triggerScenarios":"Configuring multiple clientCertificates entries for the same origin where at least one has noCertificate: true and at least one has cert/key/pfx — e.g. [{ origin: 'https://api.example.com', noCertificate: true }, { origin: 'https://api.example.com', cert: '...', key: '...' }].","commonSituations":"Test config merging (e.g. projects with different cert options concatenated) producing mixed entries for a shared origin; environment-specific overrides adding a noCertificate entry alongside the base cert entry.","solutions":["Ensure all clientCertificates entries for a given origin are consistent: either all noCertificate or all carry real certificates.","Remove the redundant noCertificate entry if the origin should use client certs.","Deduplicate entries per origin after merging configuration from multiple sources."],"exampleFix":"// before\nclientCertificates: [\n  { origin: 'https://api.example.com', noCertificate: true },\n  { origin: 'https://api.example.com', cert: './c.pem', key: './k.pem' }\n]\n// after\nclientCertificates: [\n  { origin: 'https://api.example.com', cert: './c.pem', key: './k.pem' }\n]","handlingStrategy":"validation","validationCode":"const byOrigin = new Map();\nfor (const c of clientCertificates) {\n  const list = byOrigin.get(c.origin) ?? [];\n  list.push(!!c.noCertificate);\n  byOrigin.set(c.origin, list);\n}\nfor (const [origin, flags] of byOrigin) {\n  if (flags.some(Boolean) && flags.some(f => !f))\n    throw new Error(`Mixed noCertificate entries for ${origin}`);\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Normalize clientCertificates per origin right after config merge, before context creation.","Pin one policy (certs or noCertificate) per origin in your config schema."],"tags":["client-certificates","tls","socks","config-validation"],"backgroundTag":"conflicting-config-options","analyzedSha":"500c9c822ce7664539a4c8a88810048dfe090c3b","analyzedAt":"2026-09-15T07:37:15.003Z","contentChangedAt":"2026-09-15T07:37:15.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}