valyala/fasthttp · error

cannot load tls key pair from the provided cert data(%d) and

Error message

cannot load tls key pair from the provided cert data(%d) and key data(%d): %w

What it means

Returned by Server.AppendCertEmbed when tls.X509KeyPair fails to parse the in-memory certificate/key byte slices. Same as the file-based variant but for embedded data; the wrapped error includes the byte lengths of the provided data. The server aborts TLS setup because the pair is invalid.

Source

Thrown at server.go:1957

	if err != nil {
		return err
	}

	s.mu.Lock()
	s.appendCertLocked(&cert)
	s.mu.Unlock()

	return nil
}

func x509KeyPair(certData, keyData []byte) (tls.Certificate, error) {
	if len(certData) == 0 && len(keyData) == 0 {
		return tls.Certificate{}, errNoCertOrKeyProvided
	}

	cert, err := tls.X509KeyPair(certData, keyData)
	if err != nil {
		return tls.Certificate{}, fmt.Errorf("cannot load tls key pair from the provided cert data(%d) and key data(%d): %w",
			len(certData), len(keyData), err)
	}
	return cert, nil
}

func (s *Server) appendCertLocked(cert *tls.Certificate) {
	s.configTLS()
	s.TLSConfig.Certificates = append(s.TLSConfig.Certificates, *cert)
}

func (s *Server) configTLS() {
	if s.TLSConfig == nil {
		s.TLSConfig = &tls.Config{}
	}
}

// DefaultConcurrency is the maximum number of concurrent connections
// the Server may serve by default (i.e. if Server.Concurrency isn't set).

View on GitHub (pinned to c96f600972)

Solutions

  1. Check the byte slices are valid PEM: they should start with -----BEGIN CERTIFICATE----- / -----BEGIN ... PRIVATE KEY----- (use bytes.HasPrefix to assert before calling).
  2. Confirm cert and key are not swapped and form a matching pair (compare moduli via openssl).
  3. Decrypt password-protected keys at build time or store unencrypted keys with restricted access.
  4. Fix go:embed patterns to include the correct files and verify with a small test parsing them via tls.X509KeyPair.

Example fix

// before
certData, _ := assets.ReadFile("wrong_cert.bin")
app.AppendCertEmbed(certData, keyData)
// after
certData, _ := assets.ReadFile("certs/fullchain.pem")
keyData, _ := assets.ReadFile("certs/privkey.pem")
if err := app.AppendCertEmbed(certData, keyData); err != nil { log.Fatal(err) }
Defensive patterns

Strategy: validation

Validate before calling

func validateCertData(certData, keyData []byte) error {
    if !bytes.HasPrefix(certData, []byte("-----BEGIN")) { return errors.New("cert is not PEM") }
    if !bytes.Contains(keyData, []byte("PRIVATE KEY")) { return errors.New("key is not PEM") }
    if _, err := tls.X509KeyPair(certData, keyData); err != nil { return err }
    return nil
}

Try / catch

if err := app.AppendCertEmbed(certData, keyData); err != nil {
    if strings.Contains(err.Error(), "provided cert data") {
        log.Fatalf("embedded cert/key invalid (PEM? swapped?): %v", err)
    }
    log.Fatal(err)
}

Prevention

When it happens

Trigger: Calling AppendCertEmbed with certData/keyData that are not valid PEM, don't form a matching pair, are swapped (cert passed as key), are empty-prefixed/garbage bytes, or come from go:embed paths embedding the wrong files.

Common situations: Embedding encrypted (password-protected) keys; passing DER-encoded blobs instead of PEM; accidentally embedding the CSR or fullchain in the key slot; build pipeline injecting placeholder bytes.

Understand the failure class

Related errors


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/bd83d08dab2f26d8. Report an issue: GitHub.