grpc/grpc-go · error

credentials: failed to append certificates

Error message

credentials: failed to append certificates

What it means

NewClientTLSFromFileWithALPNDisabled read the cert file but x509.CertPool.AppendCertsFromPEM returned false, meaning the file contained no PEM-encoded certificate blocks. The error is returned with no underlying cause, so the file path and contents are the only lead. This package (experimental/credentials) exists only for clients that violate HTTP/2 ALPN; prefer the stable credentials package when possible.

Solutions

  1. Open certFile and confirm it contains at least one '-----BEGIN CERTIFICATE-----' ... '-----END CERTIFICATE-----' block.
  2. If the file is DER, convert with: openssl x509 -inform DER -in ca.der -out ca.pem -outform PEM, then pass ca.pem.
  3. Make sure you are passing the root CA file, not the private key or server leaf cert.
  4. Re-fetch/regenerate the CA bundle to rule out truncation or copy corruption.
  5. Where ALPN violations are not in play, switch to credentials.NewClientTLSFromFile (stable API) which has the same check and clearer error context.

Example fix

// before
creds, err := expcreds.NewClientTLSFromFileWithALPNDisabled("server.key", "example.com")

// after
creds, err := expcreds.NewClientTLSFromFileWithALPNDisabled("ca.pem", "example.com")
Defensive patterns

Strategy: validation

Validate before calling

func loadPEMCertPool(path string) (*x509.CertPool, error) {
    b, err := os.ReadFile(path)
    if err != nil {
        return nil, err
    }
    if !bytes.Contains(b, []byte("BEGIN CERTIFICATE")) {
        return nil, fmt.Errorf("%s contains no PEM CERTIFICATE block", path)
    }
    pool := x509.NewCertPool()
    if !pool.AppendCertsFromPEM(b) {
        return nil, fmt.Errorf("%s: no usable PEM certificates", path)
    }
    return pool, nil
}

Try / catch

creds, err := expcreds.NewClientTLSFromFileWithALPNDisabled(certFile, serverName)
if err != nil {
    if strings.Contains(err.Error(), "failed to append certificates") {
        log.Fatalf("cert file %q is not a valid PEM CA bundle; convert DER->PEM and ensure it has CERTIFICATE blocks", certFile)
    }
    log.Fatalf("tls creds: %v", err)
}

Prevention

When it happens

Trigger: Calling NewClientTLSFromFileWithALPNDisabled(certFile, serverName) where certFile is empty, contains comments/whitespace only, is the private key instead of the CA cert, is in DER (binary) rather than PEM format, or holds malformed/truncated PEM blocks.

Common situations: Swapping the cert and key arguments or passing the server cert when a root CA is expected; downloading a CA bundle that was served as DER; copy-paste introducing a corrupted BEGIN/END CERTIFICATE fence; file truncated by CI artifact transfer; PEM with only a key block (BEGIN PRIVATE KEY) and no CERTIFICATE block.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/3c0a3bf022490e77. Report an issue: GitHub.

Appendix: source

Thrown at experimental/credentials/tls.go:205

	return NewTLSWithALPNDisabled(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
}

// NewClientTLSFromFileWithALPNDisabled 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. ALPN verification is disabled.
func NewClientTLSFromFileWithALPNDisabled(certFile, serverNameOverride string) (credentials.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 NewTLSWithALPNDisabled(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
}

// NewServerTLSFromCertWithALPNDisabled constructs TLS credentials from the
// input certificate for server. ALPN verification is disabled.
func NewServerTLSFromCertWithALPNDisabled(cert *tls.Certificate) credentials.TransportCredentials {
	return NewTLSWithALPNDisabled(&tls.Config{Certificates: []tls.Certificate{*cert}})
}

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

View on GitHub (pinned to 0c51461d27)