nsqio/nsq · error

failed to append certificate to pool

Error message

failed to append certificate to pool

What it means

nsqd builds its server-side TLS config in buildTLSConfig (nsqd/nsqd.go). When --tls-root-ca-file is set, it reads the file and calls x509.CertPool.AppendCertsFromPEM to seed ClientCAs, which is used to verify client certificates when --tls-client-auth-policy requires or requests client certs. AppendCertsFromPEM returns false (no error value) when the file contains no parseable CERTIFICATE PEM blocks, so nsqd cannot build the pool and startup aborts. The file was read successfully; only its contents are wrong.

Source

Thrown at nsqd/nsqd.go:776

		tlsClientAuthPolicy = tls.RequireAnyClientCert
	case "require-verify":
		tlsClientAuthPolicy = tls.RequireAndVerifyClientCert
	}

	tlsConfig = &tls.Config{
		Certificates: []tls.Certificate{cert},
		ClientAuth:   tlsClientAuthPolicy,
		MinVersion:   opts.TLSMinVersion,
	}

	if opts.TLSRootCAFile != "" {
		tlsCertPool := x509.NewCertPool()
		caCertFile, err := os.ReadFile(opts.TLSRootCAFile)
		if err != nil {
			return nil, err
		}
		if !tlsCertPool.AppendCertsFromPEM(caCertFile) {
			return nil, errors.New("failed to append certificate to pool")
		}
		tlsConfig.ClientCAs = tlsCertPool
	}

	return tlsConfig, nil
}

func buildClientTLSConfig(opts *Options) (*tls.Config, error) {
	tlsConfig := &tls.Config{
		MinVersion: opts.TLSMinVersion,
	}

	if opts.TLSRootCAFile != "" {
		tlsCertPool := x509.NewCertPool()
		caCertFile, err := os.ReadFile(opts.TLSRootCAFile)
		if err != nil {
			return nil, err
		}

View on GitHub (pinned to 85cf10c09c)

Solutions

  1. Verify the file actually contains PEM certificates: 'openssl x509 -in /path/ca.pem -noout -subject' (for DER use 'openssl x509 -inform der -in ca.der -out ca.pem' to convert).
  2. Check you referenced the CA certificate, not the server key/cert or a CSR, in --tls-root-ca-file.
  3. If the file is a bundle, confirm each block parses: 'grep -c "BEGIN CERTIFICATE" ca.pem' should be >= 1 and openssl should read every block.
  4. Re-run nsqd; startup now proceeds past TLS config.

Example fix

# before (DER file or wrong file)
nsqd --tls-root-ca-file=/etc/nsq/ca.crt   # ca.crt is DER-encoded -> AppendCertsFromPEM fails

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

Strategy: validation

Validate before calling

// pre-flight: ensure the CA file nsqd will load parses as PEM certificates
func validPEMCAs(path string) bool {
    data, err := os.ReadFile(path)
    if err != nil {
        return false
    }
    return x509.NewCertPool().AppendCertsFromPEM(data)
}

if !validPEMCAs(opts.TLSRootCAFile) {
    return fmt.Errorf("%s is not a PEM CA bundle; convert DER or fix the file", opts.TLSRootCAFile)
}

Try / catch

// in process supervisors / test harnesses around nsqd startup
if err := nsqd.New(opts); err != nil {
    if strings.Contains(err.Error(), "failed to append certificate to pool") {
        log.Printf("TLS CA file %s is not valid PEM — regenerate/convert it", opts.TLSRootCAFile)
    }
    return err
}

Prevention

When it happens

Trigger: Starting nsqd with --tls-root-ca-file pointing at a file whose contents are not PEM CERTIFICATE blocks: a DER/binary CA cert, a PEM with only PRIVATE KEY or CSR blocks, an empty or truncated file, or a file with mangled BEGIN/END lines or Windows line-corruption.

Common situations: Ops copies the server key/cert instead of the CA cert into the path; a cert downloaded from an internal CA arrives as DER (.crt binary) while Go only accepts PEM; concatenating files and losing the footer line '-----END CERTIFICATE-----'; passing the fullchain in a format produced by a different tool.

Understand the failure class

Related errors


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