gofiber/fiber · critical

failed to parse client CA certificate from %q

Error message

failed to parse client CA certificate from %q

What it means

Returned by applyClientCert when the file referenced by the mTLS client CA config was read successfully but crypto/x509's AppendCertsFromPEM rejected its contents. The file must contain one or more PEM-encoded ('-----BEGIN CERTIFICATE-----') CA certificates; anything else (DER bytes, a private key alone, a CRL, or corrupt/truncated PEM) makes the parser return false. Because the value is needed to populate tls.Config.ClientCAs for RequireAndVerifyClientCert, Fiber fails closed and returns the error at server startup rather than silently weakening client verification.

Source

Thrown at listen.go:314

	}

	served = true
	return app.server.Serve(ln)
}

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...)
func (app *App) Listener(ln net.Listener, config ...ListenConfig) error {
	cfg := listenConfigDefault(config...)

	// Graceful shutdown
	if cfg.GracefulContext != nil {
		ctx, cancel := context.WithCancel(cfg.GracefulContext)
		defer cancel()

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Verify the file is PEM: run 'openssl x509 -in <file> -noout -text' (PEM works, DER errors); if it prints 'unable to load certificate', convert with 'openssl x509 -inform DER -in <file> -out <file>.pem'.
  2. Confirm the file contains a CA certificate (has BASICCONSTRAINTS CA:TRUE) and not just a leaf or a key: 'openssl x509 -in <file> -noout -text | grep -i CA:'.
  3. Check for truncation or copy/paste corruption: 'grep -c BEGIN CERTIFICATE <file>' should be >= 1 and each BEGIN must have a matching END.
  4. Point ListenConfig.ClientCertFile at the corrected PEM path and restart so applyClientCert re-parses.

Example fix

// before
app.Listen(":443", fiber.ListenConfig{
  TLSConfig: &tls.Config{}, // ClientCertFile pointed at a DER .cer
  ClientCertFile: "/etc/ssl/client-ca.cer",
})

// after: convert to PEM and supply a CA bundle
// $ openssl x509 -inform DER -in client-ca.cer -out client-ca.pem
app.Listen(":443", fiber.ListenConfig{
  TLSConfig:      &tls.Config{},
  ClientCertFile: "/etc/ssl/client-ca.pem",
})
Defensive patterns

Strategy: validation

Validate before calling

// Validate the client CA file is PEM-parseable before calling app.Listen.
func validateClientCAPEM(path string) error {
    b, err := os.ReadFile(filepath.Clean(path))
    if err != nil {
        return fmt.Errorf("read CA file: %w", err)
    }
    pool := x509.NewCertPool()
    if !pool.AppendCertsFromPEM(b) {
        return fmt.Errorf("file %q is not PEM-encoded CA certificate(s)", path)
    }
    // optional: ensure at least one CA:TRUE cert
    return nil
}

// at startup:
if err := validateClientCAPEM(cfg.ClientCertFile); err != nil {
    log.Fatal(err)
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Configuring ListenConfig with a ClientCertFile path whose bytes are not parseable PEM certificates (e.g. a DER-encoded .cer, a PEM containing only a private key, an empty file, or a file with a broken '-----END CERTIFICATE-----' boundary). AppendCertsFromPEM returns false, so applyClientCert returns this exact error before any listener is created.

Common situations: Exporting a CA from a browser or Windows certmgr often yields DER; ops copies the wrong file (leaf cert instead of CA, or a combined bundle that starts with a key); CI mounts an empty secret because the Kubernetes Secret name was mistyped; converting formats with openssl forgets the -outform PEM flag.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/42750b21801559d5.json. Report an issue: GitHub.