gofiber/fiber · critical

failed to read client CA file

Error message

failed to read client CA file %q: %w

What it means

Returned by applyClientCert when os.ReadFile fails to read the file pointed to by ListenConfig.CertClientFile. Fiber reads this PEM file to populate the client CA pool used for mutual TLS (tls.RequireAndVerifyClientCert). The wrapped error preserves the underlying os/fs error (ENOENT, EACCES, EISDIR, etc.).

Solutions

  1. Verify the path exists and is a regular file: ls -l <path> and file <path>.
  2. Check the process can read it under the runtime user: sudo -u <user> cat <path> >/dev/null.
  3. Use an absolute path for CertClientFile; never rely on the service's working directory.
  4. If running in systemd/Docker, confirm the file is COPY'd/ADD'd into the image and the volume is mounted read-only.
  5. Ensure the file is PEM-encoded; a DER cert will pass ReadFile but fail later at AppendCertsFromPEM (error 121).

Example fix

// before
cfg := fiber.ListenConfig{CertClientFile: "ca.pem"} // relative, missing in prod
app.Listen(":443", cfg)

// after
cfg := fiber.ListenConfig{CertClientFile: "/etc/fiber/tls/client-ca.pem"}
app.Listen(":443", cfg)
Defensive patterns

Strategy: validation

Validate before calling

// Run before app.Listen to fail fast with a clearer message.
func checkClientCAFile(path string) error {
    if path == "" {
        return nil // not configured; fiber skips mTLS
    }
    abs, err := filepath.Abs(path)
    if err != nil {
        return fmt.Errorf("resolve CertClientFile path: %w", err)
    }
    info, err := os.Stat(abs)
    if err != nil {
        return fmt.Errorf("CertClientFile unreadable: %w", err)
    }
    if info.IsDir() {
        return fmt.Errorf("CertClientFile %q is a directory", abs)
    }
    if info.Mode().Perm()&0o400 == 0 {
        return fmt.Errorf("CertClientFile %q not readable by current user", abs)
    }
    return nil
}

Prevention

When it happens

Trigger: App.Listen or App.ListenTLS is called with ListenConfig.CertClientFile set to a path that does not exist, is unreadable under the process's UID/GID, is a directory, or lives on an unmounted filesystem. The error fires before any listener is created, during createListener -> applyClientCert.

Common situations: Deploying behind mTLS with a path that works in dev but is missing in the container image; running the binary as a non-root user that cannot read a root-owned CA file; relative path resolved against the wrong working directory; typo in the env var feeding CertClientFile (e.g. $TLS_CA_CERT vs $TLS_CLIENT_CERT).

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/f9ef8138c599df7f. Report an issue: GitHub.

Appendix: source

Thrown at listen.go:399

	log.Warnf("[Listener] serves the supplied listener as-is, so %s %s ignored%s",
		strings.Join(ignored, ", "), pluralIsAre(len(ignored)), suffix)
}

func pluralIsAre(n int) string {
	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

View on GitHub (pinned to a105acad6c)