thanos-io/thanos · error
building gRPC client
Error message
building gRPC client
What it means
grpcClientConfig.dialOptions builds client-side gRPC dial options via extgrpc.StoreClientGRPCOpts, which assembles TLS credentials from the configured cert/key/CA files. When credential loading fails (unreadable files, bad PEM, invalid key pair), the error is wrapped as 'building gRPC client' and Thanos client setup aborts at startup.
Solutions
- Verify each TLS file path exists and is readable by the Thanos process; check k8s secret mounts and permissions.
- Validate that cert and key match: 'openssl x509 -noout -modulus -in cert.pem' vs 'openssl rsa -noout -modulus -in key.pem'.
- Confirm PEM contents are valid: 'openssl x509 -in cert.pem -text' and 'openssl verify -CAfile ca.pem cert.pem'.
- Migrate deprecated --grpc-client-tls-* flags to the YAML grpc client configuration (deprecated after v0.43.0), then restart.
Example fix
// before --grpc-client-tls-cert=/etc/thanos/client.crt --grpc-client-tls-key=/etc/thanos/client.key --grpc-client-tls-ca=/etc/thanos/ca.crt // (fails: /etc/thanos/ca.crt missing) // after --grpc-client-tls-cert=/etc/thanos/tls/client.crt --grpc-client-tls-key=/etc/thanos/tls/client.key --grpc-client-tls-ca=/etc/thanos/tls/ca.crt
Defensive patterns
Strategy: validation
Validate before calling
// Validate TLS material before dialing
func validateTLS(cert, key, ca string) error {
for _, p := range []string{cert, key, ca} {
if p == "" { continue }
f, err := os.Open(p)
if err != nil { return fmt.Errorf("cannot open %s: %w", p, err) }
f.Close()
}
if cert != "" && key != "" {
if _, err := tls.LoadX509KeyPair(cert, key); err != nil {
return fmt.Errorf("invalid key pair: %w", err)
}
}
return nil
} Type guard
func tlsFilesReadable(cert, key, ca string) bool {
for _, p := range []string{cert, key, ca} {
if p != "" {
if _, err := os.Stat(p); err != nil { return false }
}
}
return true
} Try / catch
dialOpts, err := gc.dialOptions(logger, reg, tracer)
if err != nil {
return nil, errors.Wrap(err, "building gRPC client") // log full inner cause: file path + reason
} Prevention
- Mount TLS files via secrets and verify paths/permissions in the deployment manifest.
- Check cert/key pair match and expiry in CI before rollout.
- Migrate off deprecated --grpc-client-tls-* flags to YAML grpc config (post v0.43.0).
- Smoke-test client connections with the same TLS material in a pre-prod check.
When it happens
Trigger: StoreClientGRPCOpts returns an error — cert, key, or CA file passed via --grpc-client-tls-cert/--grpc-client-tls-key/--grpc-client-tls-ca does not exist, is unreadable, or contains invalid PEM; or cert+key do not form a valid tls.X509KeyPair.
Common situations: Typo in TLS file paths, files mounted but with wrong permissions in Kubernetes secrets, expired/mismatched cert/key pairs, or using deprecated TLS flags after the v0.43.0 switch to YAML-based grpc client config without migrating.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- setup gRPC server
- TSDB statistics not implemented
- raw resolution must be higher than the minimum block size…
- 5m resolution retention must be higher than the minimum…
- endpoint
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/56b2f2d806cac4cb.
Report an issue: GitHub.
Appendix: source
Thrown at cmd/thanos/config.go:104
func (gc *grpcClientConfig) registerFlag(cmd extkingpin.FlagClause) *grpcClientConfig {
cmd.Flag("grpc-client-tls-secure", "Deprecated after v0.43.0: Use TLS when talking to the gRPC server").Default("false").BoolVar(&gc.secure)
cmd.Flag("grpc-client-tls-skip-verify", "Deprecated after v0.43.0: Disable TLS certificate verification i.e self signed, signed by fake CA").Default("false").BoolVar(&gc.skipVerify)
cmd.Flag("grpc-client-tls-cert", "Deprecated after v0.43.0: TLS Certificates to use to identify this client to the server").Default("").StringVar(&gc.cert)
cmd.Flag("grpc-client-tls-key", "Deprecated after v0.43.0: TLS Key for the client's certificate").Default("").StringVar(&gc.key)
cmd.Flag("grpc-client-tls-ca", "Deprecated after v0.43.0: TLS CA Certificates to use to verify gRPC servers").Default("").StringVar(&gc.caCert)
cmd.Flag("grpc-client-server-name", "Deprecated after v0.43.0: Server name to verify the hostname on the returned gRPC certificates. See https://tools.ietf.org/html/rfc4366#section-3.1").Default("").StringVar(&gc.serverName)
compressionOptions := strings.Join([]string{snappy.Name, compressionNone}, ", ")
cmd.Flag("grpc-compression", "Deprecated after v0.43.0: Compression algorithm to use for gRPC requests to other clients. Must be one of: "+compressionOptions).Default(compressionNone).EnumVar(&gc.compression, snappy.Name, compressionNone)
cmd.Flag("grpc-client-tls-min-version",
"Deprecated after v0.43.0: TLS supported minimum version for gRPC client. If no version is specified, it'll default to 1.3. Allowed values: [\"1.0\", \"1.1\", \"1.2\", \"1.3\"]").
Default("1.3").EnumVar(&gc.minTLSVersion, tls.AllowedTLSVersions...)
return gc
}
func (gc *grpcClientConfig) dialOptions(logger log.Logger, reg prometheus.Registerer, tracer opentracing.Tracer) ([]grpc.DialOption, error) {
dialOpts, err := extgrpc.StoreClientGRPCOpts(logger, reg, tracer)
if err != nil {
return nil, errors.Wrapf(err, "building gRPC client")
}
return dialOpts, nil
}
type httpConfig struct {
bindAddress string
tlsConfig string
gracePeriod model.Duration
}
func (hc *httpConfig) registerFlag(cmd extkingpin.FlagClause) *httpConfig {
cmd.Flag("http-address",
"Listen host:port for HTTP endpoints.").
Default("0.0.0.0:10902").StringVar(&hc.bindAddress)
cmd.Flag("http-grace-period",
"Time to wait after an interrupt received for HTTP Server.").
Default("2m").SetValue(&hc.gracePeriod)
cmd.Flag(View on GitHub (pinned to 35b8b99117)