{"record":{"id":"3f0adfc7e5831358","repo":"t8y2/dbx","slug":"client-certificate-and-key-must-be-provided-togeth-3f0adf","errorCode":null,"errorMessage":"Client certificate and key must be provided together","messagePattern":"Client certificate and key must be provided together","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/drivers/etcd2-go/client.go","lineNumber":192,"sourceCode":"}\n\nfunc tlsConfigFor(connection connectionParams) (*tls.Config, error) {\n\ttlsConfig := &tls.Config{}\n\tif ca := strings.TrimSpace(connection.CACertPath); ca != \"\" {\n\t\tauthorityPEM, err := os.ReadFile(ca)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpool := x509.NewCertPool()\n\t\tif !pool.AppendCertsFromPEM(authorityPEM) {\n\t\t\treturn nil, fmt.Errorf(\"failed to parse CA certificate at %s\", ca)\n\t\t}\n\t\ttlsConfig.RootCAs = pool\n\t}\n\tcertPath := firstNonBlank(connection.ClientCertPath, connection.CertPath)\n\tkeyPath := firstNonBlank(connection.ClientKeyPath, connection.KeyPath)\n\tif (certPath == \"\") != (keyPath == \"\") {\n\t\treturn nil, errors.New(\"Client certificate and key must be provided together\")\n\t}\n\tif certPath != \"\" {\n\t\tpair, err := tls.LoadX509KeyPair(certPath, keyPath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.Certificates = []tls.Certificate{pair}\n\t}\n\treturn tlsConfig, nil\n}\n\n// probeClient verifies the endpoint speaks the v2 API. It returns the\n// connected client and a probe result shaped like the v3 agent's:\n// {ok, endpoint, limited?}.\nfunc probeClient(endpoint string, connection connectionParams) (*authenticatedClient, map[string]any, error) {\n\thttpClient, err := buildHTTPClient(connection)\n\tif err != nil {\n\t\treturn nil, nil, err","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/drivers/etcd2-go/client.go#L174-L210","documentation":"tlsConfigFor builds the TLS configuration and requires mTLS material to be complete. A client certificate without its key, or a key without its certificate, can never form a valid TLS key pair, so the combination is rejected with this error. Cert and key may come from either ClientCertPath/ClientKeyPath or the legacy CertPath/KeyPath aliases.","triggerScenarios":"Setting connection.ClientCertPath (or CertPath) without ClientKeyPath (or KeyPath), or vice versa, when building the HTTP client for an etcd v2 connection.","commonSituations":"Partial mTLS config in a YAML/JSON file where one path was edited or deleted; secret mounts where only one file was mounted; copying cert path but forgetting the key path in env vars.","solutions":["Provide both certPath and keyPath (each resolves via firstNonBlank from Client*/Cert* and Client*/Key* aliases)","If mTLS is not required, omit both paths so the plain-TLS path is used","Verify both files exist and are readable at the given paths"],"exampleFix":"// before\nconnection.ClientCertPath = \"/etc/ssl/client.crt\" // key missing\n// after\nconnection.ClientCertPath = \"/etc/ssl/client.crt\"\nconnection.ClientKeyPath  = \"/etc/ssl/client.key\"","handlingStrategy":"validation","validationCode":"certPath := firstNonBlank(conn.ClientCertPath, conn.CertPath)\nkeyPath := firstNonBlank(conn.ClientKeyPath, conn.KeyPath)\nif (certPath == \"\") != (keyPath == \"\") {\n\treturn errors.New(\"mTLS config incomplete: provide BOTH client cert and key paths\")\n}\nif certPath != \"\" {\n\tif _, err := os.Stat(certPath); err != nil { return err }\n\tif _, err := os.Stat(keyPath); err != nil { return err }\n}","typeGuard":"func mtlsComplete(c *Connection) bool {\n\tcert := firstNonBlank(c.ClientCertPath, c.CertPath)\n\tkey := firstNonBlank(c.ClientKeyPath, c.KeyPath)\n\treturn (cert == \"\") == (key == \"\")\n}","tryCatchPattern":"client, err := buildHTTPClient(conn)\nif err != nil {\n\tif strings.Contains(err.Error(), \"provided together\") {\n\t\treturn fmt.Errorf(\"mTLS misconfiguration: set both client cert and key paths\")\n\t}\n\treturn err\n}","preventionTips":["Treat cert+key as one atomic config unit; set both in the same change","Validate mTLS completeness in config loading, before any connection attempt","Check both files are mounted (secret volumes often mount only one)","Cover with a test: cert-without-key and key-without-cert must both fail fast"],"tags":["etcd","tls","mtls","configuration"],"backgroundTag":"tls-cert-key-mismatch","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}