{"record":{"id":"3db25792a347176a","repo":"thanos-io/thanos","slug":"both-client-key-and-certificate-must-be-provided","errorCode":null,"errorMessage":"both client key and certificate must be provided","messagePattern":"both client key and certificate must be provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/tls/options.go","lineNumber":181,"sourceCode":"\t\t\treturn nil, err\n\t\t}\n\t\tlevel.Debug(logger).Log(\"msg\", fmt.Sprintf(\"setting minimum TLS version to %s\", minTLSVersion))\n\t}\n\ttlsCfg := &tls.Config{\n\t\tRootCAs:    certPool,\n\t\tMinVersion: mtlsVersion,\n\t}\n\n\tif serverName != \"\" {\n\t\ttlsCfg.ServerName = serverName\n\t}\n\n\tif skipVerify {\n\t\ttlsCfg.InsecureSkipVerify = true\n\t}\n\n\tif (key != \"\") != (cert != \"\") {\n\t\treturn nil, errors.New(\"both client key and certificate must be provided\")\n\t}\n\n\tif cert != \"\" {\n\t\tmngr := &clientTLSManager{\n\t\t\tcertPath: cert,\n\t\t\tkeyPath:  key,\n\t\t}\n\t\ttlsCfg.GetClientCertificate = mngr.getClientCertificate\n\n\t\tlevel.Debug(logger).Log(\"msg\", \"TLS client authentication enabled\")\n\t}\n\treturn tlsCfg, nil\n}\n\ntype clientTLSManager struct {\n\tcertPath string\n\tkeyPath  string\n","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/tls/options.go#L163-L199","documentation":"NewClientConfig returns this error when exactly one of cert and key is non-empty. Mutual TLS requires the client certificate and its private key to be supplied together; a lone half is a configuration error.","triggerScenarios":"StoreClientTLSCredentials calls NewClientConfig with cert set but key empty (or vice versa) when configuring mTLS.","commonSituations":"Only the cert file was mounted/secret-shared, the key was omitted for security; one of the two env vars/flags misnamed; TLS config template filled partially; copy-paste dropped the key path line.","solutions":["Provide both cert and key paths together, or neither.","Check the corresponding flags/env vars are both set (e.g. TLS_CERT and TLS_KEY).","Mount both files from the secret and pass both paths.","If mTLS is not required, omit both to use an unauthenticated client."],"exampleFix":"// before\ncfg, err := tls.NewClientConfig(logger, certPath, \"\", caPath, serverName, false, \"1.2\")\n// after\nif (certPath == \"\") != (keyPath == \"\") {\n    return errors.New(\"client cert and key must both be set\")\n}\ncfg, err := tls.NewClientConfig(logger, certPath, keyPath, caPath, serverName, false, \"1.2\")","handlingStrategy":"validation","validationCode":"func havePair(cert, key string) error {\n    if (cert == \"\") != (key == \"\") {\n        return errors.New(\"client cert and key must be provided together\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := havePair(certPath, keyPath); err != nil {\n    return err\n}\ncfg, err := tls.NewClientConfig(logger, certPath, keyPath, caCert, serverName, skipVerify, ver)","preventionTips":["Group cert+key under one config section/secret so they travel together.","Validate both fields in config loading before calling the library.","Use a single env var prefix (TLS_CERT_*/TLS_KEY_*) and check both.","Fail at startup, not on first connection."],"tags":["tls","mtls","config","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}