{"record":{"id":"a3cfe696372b76f5","repo":"hyperledger/fabric","slug":"cannot-load-client-cert-for-consenter-s-d-s","errorCode":null,"errorMessage":"cannot load client cert for consenter %s:%d: %s","messagePattern":"cannot load client cert for consenter (.+?):(.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/channelconfig/util.go","lineNumber":319,"sourceCode":"\t\treturn nil, errors.Errorf(\"invalid configuration block, missing %s configuration group\", ApplicationGroupKey)\n\t}\n\n\tcc, err := NewChannelConfig(configEnv.Config.ChannelGroup, bccsp)\n\tif err != nil {\n\t\treturn nil, errors.WithMessage(err, \"no valid channel configuration found\")\n\t}\n\treturn cc, nil\n}\n\n// MarshalEtcdRaftMetadata serializes etcd RAFT metadata.\nfunc MarshalEtcdRaftMetadata(md *etcdraft.ConfigMetadata) ([]byte, error) {\n\tcopyMd := proto.Clone(md).(*etcdraft.ConfigMetadata)\n\tfor _, c := range copyMd.Consenters {\n\t\t// Expect the user to set the config value for client/server certs to the\n\t\t// path where they are persisted locally, then load these files to memory.\n\t\tclientCert, err := os.ReadFile(string(c.GetClientTlsCert()))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot load client cert for consenter %s:%d: %s\", c.GetHost(), c.GetPort(), err)\n\t\t}\n\t\tc.ClientTlsCert = clientCert\n\n\t\tserverCert, err := os.ReadFile(string(c.GetServerTlsCert()))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"cannot load server cert for consenter %s:%d: %s\", c.GetHost(), c.GetPort(), err)\n\t\t}\n\t\tc.ServerTlsCert = serverCert\n\t}\n\treturn proto.Marshal(copyMd)\n}\n\n// MarshalBFTOptions serializes smartbft options.\nfunc MarshalBFTOptions(op *smartbft.Options) ([]byte, error) {\n\tif copyMd, ok := proto.Clone(op).(*smartbft.Options); ok {\n\t\treturn proto.Marshal(copyMd)\n\t} else {\n\t\treturn nil, errors.New(\"consenter options type mismatch\")","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/channelconfig/util.go#L301-L337","documentation":"MarshalEtcdRaftMetadata converts an etcdraft.ConfigMetadata whose consenters hold local file paths for TLS certs into one holding raw cert bytes by reading the files with os.ReadFile. This error wraps the read failure for a consenter's client TLS cert, including the host, port, and underlying OS error. It surfaces during channel config creation/orderer group validation when the paths are wrong or unreadable.","triggerScenarios":"NewOrdererGroup (or the unit test) invoked with etcdraft metadata where a consenter's ClientTlsCert field contains a path that does not exist, is a directory, or is unreadable by the process.","commonSituations":"Running configtxgen/orderer on a machine where the generated cert paths don't exist; relative paths resolved from a different working directory; containerized deployments where host paths were not mounted; permissions changed after cert rotation.","solutions":["Fix the client_tls_cert path in the consenter entry (configtx.yaml / raft metadata) to point at an existing file readable by the process.","Run from a working directory where the relative cert paths resolve, or switch to absolute paths.","In containers, mount the certificate directory and verify with `ls -l <path>` inside the container.","Check file permissions (the process user needs read access) and re-read the OS error embedded in the message for the exact cause (ENOENT, EACCES, EISDIR)."],"exampleFix":"// configtx.yaml before\nConsenter:\n  - Host: raft0\n    Port: 7050\n    ClientTLSCert: crypto-config/peerOrganizations/tls/server.crt\n// after (path that exists relative to configtxgen cwd)\nConsenter:\n  - Host: raft0\n    Port: 7050\n    ClientTLSCert: ./crypto-config/ordererOrganizations/example.com/orderers/raft0.example.com/tls/server.crt","handlingStrategy":"validation","validationCode":"import \"os\"\n\nfunc validateConsenterCerts(md *etcdraft.ConfigMetadata) error {\n\tfor _, c := range md.Consenters {\n\t\tif fi, err := os.Stat(string(c.GetClientTlsCert())); err != nil {\n\t\t\treturn fmt.Errorf(\"client cert %q unreadable: %w\", c.GetClientTlsCert(), err)\n\t\t} else if fi.IsDir() {\n\t\t\treturn fmt.Errorf(\"client cert %q is a directory\", c.GetClientTlsCert())\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"// errors are returned, not panicked; wrap the call\nmd, err := channelconfig.MarshalEtcdRaftMetadata(metadata)\nif err != nil {\n\tvar pe *fs.PathError\n\tif errors.As(err, &pe) { /* fix cert path: %q -> %v */ }\n\treturn fmt.Errorf(\"raft metadata: %w\", err)\n}","preventionTips":["Use absolute paths for TLS certs in configtx.yaml","Verify cert paths exist and are readable by the process user before generating genesis blocks","In containers, mount the cert directory and verify inside the container","Log the wrapped OS error (ENOENT/EACCES) to distinguish missing vs permission issues"],"tags":["hyperledger-fabric","etcdraft","tls","file-io"],"backgroundTag":"tls-cert-file-not-found","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}