nsqio/nsq · critical

failed to build client TLS config - %s

Error message

failed to build client TLS config - %s

What it means

Separately from the server config, nsqd.New builds a client TLS config used for its own outbound TLS connections (e.g. to nsqlookupd). buildClientTLSConfig only touches --tls-root-ca-file: it reads the file and must parse it as PEM into the root pool ('failed to append certificate to pool' otherwise). Unreadable or non-PEM files produce this wrapped error.

Source

Thrown at nsqd/nsqd.go:134

		return nil, errors.New("--node-id must be [0,1024)")
	}

	if opts.TLSClientAuthPolicy != "" && opts.TLSRequired == TLSNotRequired {
		opts.TLSRequired = TLSRequired
	}

	tlsConfig, err := buildTLSConfig(opts)
	if err != nil {
		return nil, fmt.Errorf("failed to build TLS config - %s", err)
	}
	if tlsConfig == nil && opts.TLSRequired != TLSNotRequired {
		return nil, errors.New("cannot require TLS client connections without TLS key and cert")
	}
	n.tlsConfig = tlsConfig

	clientTLSConfig, err := buildClientTLSConfig(opts)
	if err != nil {
		return nil, fmt.Errorf("failed to build client TLS config - %s", err)
	}
	n.clientTLSConfig = clientTLSConfig

	if opts.AuthHTTPRequestMethod != "post" && opts.AuthHTTPRequestMethod != "get" {
		return nil, errors.New("--auth-http-request-method must be post or get")
	}

	for _, v := range opts.E2EProcessingLatencyPercentiles {
		if v <= 0 || v > 1 {
			return nil, fmt.Errorf("invalid E2E processing latency percentile: %v", v)
		}
	}

	n.logf(LOG_INFO, version.String("nsqd"))
	n.logf(LOG_INFO, "ID: %d", opts.ID)

	n.tcpServer = &tcpServer{nsqd: n}
	n.tcpListener, err = net.Listen(util.TypeOfAddr(opts.TCPAddress), opts.TCPAddress)

View on GitHub (pinned to 85cf10c09c)

Solutions

  1. Confirm the file exists and is readable by the nsqd user
  2. Convert DER to PEM if needed: openssl x509 -inform DER -in ca.der -out ca.pem
  3. Validate it parses: openssl x509 -in ca.pem -noout
  4. Point --tls-root-ca-file at the PEM CA bundle that signed your lookupd certificates

Example fix

# before
--tls-root-ca-file=/etc/nsqd/ca.der   # DER, not PEM

# after
openssl x509 -inform DER -in /etc/nsqd/ca.der -out /etc/nsqd/ca.pem
--tls-root-ca-file=/etc/nsqd/ca.pem
Defensive patterns

Strategy: validation

Validate before calling

b, err := os.ReadFile(caFile)
if err != nil { log.Fatalf("cannot read CA file: %v", err) }
if !x509.NewCertPool().AppendCertsFromPEM(b) {
	log.Fatal("--tls-root-ca-file is not a PEM CA bundle")
}

Try / catch

n, err := nsqd.New(opts)
if err != nil && strings.Contains(err.Error(), "failed to build client TLS config") {
	// CA bundle unreadable/invalid: fix format or permissions, restart
}

Prevention

When it happens

Trigger: Setting --tls-root-ca-file to a path that does not exist or that the nsqd user cannot read; pointing it at a DER-encoded or otherwise non-PEM CA file; a truncated file after a config-management push.

Common situations: Self-signed lookupd PKI where the CA was exported in DER format; file permissions 0600 root:root in a container running as nobody; volume mounts that materialize the file late or empty.

Understand the failure class

Related errors


AI-assisted analysis of nsqio/nsq@85cf10c09c (2026-08-16). Data as JSON: /api/errors/92a18fe9167e3145. Report an issue: GitHub.