grpc/grpc-go · error

credentials: failed to append certificates

Error message

credentials: failed to append certificates

What it means

Returned by NewClientTLSFromFile in credentials/tls.go:290 when x509.CertPool.AppendCertsFromPEM returns false, meaning the file contents were not parseable as one or more PEM-encoded certificates. The file was readable (os.ReadFile succeeded) but the bytes are not a valid PEM cert block — e.g. a DER blob, a key file, or a non-cert PEM block.

Source

Thrown at credentials/tls.go:290

// NewClientTLSFromFile constructs TLS credentials from the provided root
// certificate authority certificate file(s) to validate server connections. If
// certificates to establish the identity of the client need to be included in
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
// tls.Config can be specified.
//
// serverNameOverride is for testing only. If set to a non empty string, it will
// override the virtual host name of authority (e.g. :authority header field) in
// requests.  Users should use grpc.WithAuthority passed to grpc.NewClient to
// override the authority of the client instead.
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
	b, err := os.ReadFile(certFile)
	if err != nil {
		return nil, err
	}
	cp := x509.NewCertPool()
	if !cp.AppendCertsFromPEM(b) {
		return nil, fmt.Errorf("credentials: failed to append certificates")
	}
	return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
}

// NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
	return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
}

// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
// file for server.
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}
	return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Regenerate/obtain the certificate in PEM format (openssl x509 -inform DER -outform PEM -in cert.der -out cert.pem).
  2. Pass the CA certificate file (-----BEGIN CERTIFICATE-----), not the private key, to NewClientTLSFromFile.
  3. For a full tls.Config including client certs, use credentials.NewTLS with a populated tls.Config instead.

Example fix

// before
creds, err := credentials.NewClientTLSFromFile("/etc/certs/server.key", "svc") // wrong file

// after
creds, err := credentials.NewClientTLSFromFile("/etc/certs/ca.pem", "svc.example.com")
Defensive patterns

Strategy: validation

Validate before calling

b, err := os.ReadFile(certFile)
if err != nil { return err }
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(b) {
    // likely DER or a non-cert PEM block; convert first
    return errors.New("not PEM; convert with: openssl x509 -inform DER -outform PEM")
}
creds := credentials.NewClientTLSFromCert(pool, serverName)

Type guard

func isPEMCert(b []byte) bool {
    return bytes.Contains(b, []byte("-----BEGIN CERTIFICATE-----"))
}

Try / catch

creds, err := credentials.NewClientTLSFromFile(certFile, serverName)
if err != nil {
    if strings.Contains(err.Error(), "failed to append certificates") {
        // file is not PEM certs; convert DER->PEM or point at the CA cert file
    }
    return err
}

Prevention

When it happens

Trigger: Passing a path to NewClientTLSFromFile whose content is a DER-encoded cert, a private key, a certificate chain in the wrong encoding, a CA bundle with extra text, or an empty file.

Common situations: Using the server key/cert file instead of the CA cert; cert generated as DER (openssl enc -outform DER) instead of PEM; pasting only the human-readable text of a PEM; an empty or truncated file from a failed download.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/677d184490ee4be4. Report an issue: GitHub.