gofiber/fiber · critical
failed to parse client CA certificate from
Error message
failed to parse client CA certificate from %q
What it means
Returned by applyClientCert when x509.CertPool.AppendCertsFromPEM rejects the bytes read from CertClientFile. AppendCertsFromPEM returns false when no PEM block of type CERTIFICATE can be decoded, so this fires for empty files, non-PEM input, PEM with the wrong block type, or a file containing only keys/CRLs. Unlike error 120, the read itself succeeded; the content is unusable as a CA bundle.
Solutions
- Convert DER to PEM: openssl x509 -in client-ca.der -inform DER -out client-ca.pem -outform PEM.
- Inspect the file: head -1 <path> should show -----BEGIN CERTIFICATE-----; verify with openssl x509 -in <path> -noout -text.
- Concatenate the full issuing chain (root + intermediates) as separate BEGIN CERTIFICATE blocks.
- Regenerate the file from your CA rather than reusing a key or CSR.
- Confirm the file is non-empty after renewal hooks run.
Example fix
// before
cfg := fiber.ListenConfig{CertClientFile: "/etc/fiber/client-ca.der"} // DER format
// after (convert once, then point at PEM)
// $ openssl x509 -in /etc/fiber/client-ca.der -inform DER -out /etc/fiber/client-ca.pem -outform PEM
cfg := fiber.ListenConfig{CertClientFile: "/etc/fiber/client-ca.pem"} Defensive patterns
Strategy: validation
Validate before calling
import "crypto/x509"
import "encoding/pem"
func validateClientCABundle(path string) error {
raw, err := os.ReadFile(path)
if err != nil { return err }
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(raw) {
// drill into why
var block *pem.Block
rest := raw
found := false
for {
block, rest = pem.Decode(rest)
if block == nil { break }
if block.Type == "CERTIFICATE" { found = true; break }
}
if !found {
return fmt.Errorf("no PEM CERTIFICATE block in %q; got first block type %q", path, firstBlockType(raw))
}
return fmt.Errorf("PEM CERTIFICATE present but rejected by x509")
}
return nil
} Prevention
- Always store CA bundles as PEM (-----BEGIN CERTIFICATE-----).
- Validate the bundle in CI with openssl x509 -in <file> -noout -text.
- Keep the CA bundle and the issuing chain concatenated as separate PEM blocks.
- Treat a zero-byte file from a failed renewal as a deployment blocker.
When it happens
Trigger: CertClientFile points to a file that is not a PEM certificate bundle: a DER-encoded cert, a PEM private key, a CSR, a CRL, an empty file, a concatenated PEM with only comments/garbage, or a PEM whose armor says BEGIN TRUSTED CERTIFICATE (OpenSSL 'trust' format) instead of BEGIN CERTIFICATE.
Common situations: Operator drops a .crt produced by openssl without -outform PEM; certificate rotated by a pipeline that writes both key and cert to the same file; file truncated to zero bytes by a failed renewal (certbot/lego) that Fiber then loads on restart; file is the client certificate itself rather than the issuing CA.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- failed to read client CA file
- tls: AutoCertManager cannot be combined with…
- unsupported TLS version, please use tls.VersionTLS12 or…
- cannot chmod %#o for
- failed to append certificate
AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11).
Data as JSON: /api/errors/42750b21801559d5.
Report an issue: GitHub.
Appendix: source
Thrown at listen.go:404
if n == 1 {
return "is"
}
return "are"
}
func applyClientCert(tlsConfig *tls.Config, certClientFile string) error {
if certClientFile == "" {
return nil
}
clientCACert, err := os.ReadFile(filepath.Clean(certClientFile))
if err != nil {
return fmt.Errorf("failed to read client CA file %q: %w", certClientFile, err)
}
clientCertPool := x509.NewCertPool()
if ok := clientCertPool.AppendCertsFromPEM(clientCACert); !ok {
return fmt.Errorf("failed to parse client CA certificate from %q", certClientFile)
}
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
tlsConfig.ClientCAs = clientCertPool
return nil
}
// Listener serves HTTP requests from the given listener.
// You should enter custom ListenConfig to customize startup. (prefork, startup message, graceful shutdown...)
//
// The listener is served exactly as supplied, so every TLS field of the config
// is ignored — including CertClientFile. Wrap it with tls.NewListener yourself
// to serve TLS or require a client certificate.
func (app *App) Listener(ln net.Listener, config ...ListenConfig) error {
cfg := listenConfigDefault(config...)
warnIgnoredTLSFieldsOnListener(&cfg, ln)
View on GitHub (pinned to a105acad6c)