{"record":{"id":"42750b21801559d5","repo":"gofiber/fiber","slug":"failed-to-parse-client-ca-certificate-from-q","errorCode":null,"errorMessage":"failed to parse client CA certificate from %q","messagePattern":"failed to parse client CA certificate from %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"listen.go","lineNumber":404,"sourceCode":"\tif n == 1 {\n\t\treturn \"is\"\n\t}\n\treturn \"are\"\n}\n\nfunc applyClientCert(tlsConfig *tls.Config, certClientFile string) error {\n\tif certClientFile == \"\" {\n\t\treturn nil\n\t}\n\n\tclientCACert, err := os.ReadFile(filepath.Clean(certClientFile))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read client CA file %q: %w\", certClientFile, err)\n\t}\n\n\tclientCertPool := x509.NewCertPool()\n\tif ok := clientCertPool.AppendCertsFromPEM(clientCACert); !ok {\n\t\treturn fmt.Errorf(\"failed to parse client CA certificate from %q\", certClientFile)\n\t}\n\n\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\ttlsConfig.ClientCAs = clientCertPool\n\n\treturn nil\n}\n\n// Listener serves HTTP requests from the given listener.\n// You should enter custom ListenConfig to customize startup. (prefork, startup message, graceful shutdown...)\n//\n// The listener is served exactly as supplied, so every TLS field of the config\n// is ignored — including CertClientFile. Wrap it with tls.NewListener yourself\n// to serve TLS or require a client certificate.\nfunc (app *App) Listener(ln net.Listener, config ...ListenConfig) error {\n\tcfg := listenConfigDefault(config...)\n\twarnIgnoredTLSFieldsOnListener(&cfg, ln)\n","sourceCodeStart":386,"sourceCodeEnd":422,"githubUrl":"https://github.com/gofiber/fiber/blob/a105acad6c1e4576a77f01e02973f67e962bb58d/listen.go#L386-L422","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncfg := fiber.ListenConfig{CertClientFile: \"/etc/fiber/client-ca.der\"} // DER format\n\n// after (convert once, then point at PEM)\n// $ openssl x509 -in /etc/fiber/client-ca.der -inform DER -out /etc/fiber/client-ca.pem -outform PEM\ncfg := fiber.ListenConfig{CertClientFile: \"/etc/fiber/client-ca.pem\"}","handlingStrategy":"validation","validationCode":"import \"crypto/x509\"\nimport \"encoding/pem\"\n\nfunc validateClientCABundle(path string) error {\n    raw, err := os.ReadFile(path)\n    if err != nil { return err }\n    pool := x509.NewCertPool()\n    if !pool.AppendCertsFromPEM(raw) {\n        // drill into why\n        var block *pem.Block\n        rest := raw\n        found := false\n        for {\n            block, rest = pem.Decode(rest)\n            if block == nil { break }\n            if block.Type == \"CERTIFICATE\" { found = true; break }\n        }\n        if !found {\n            return fmt.Errorf(\"no PEM CERTIFICATE block in %q; got first block type %q\", path, firstBlockType(raw))\n        }\n        return fmt.Errorf(\"PEM CERTIFICATE present but rejected by x509\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["tls","mtls","x509","pem","startup","listen"],"backgroundTag":null,"analyzedSha":"a105acad6c1e4576a77f01e02973f67e962bb58d","analyzedAt":"2026-08-11T17:33:26.942Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}