{"record":{"id":"3945fd7abc81f27f","repo":"gastownhall/beads","slug":"externaldoltconfig-tlscacert-q-no-certificates","errorCode":null,"errorMessage":"ExternalDoltConfig: TLSCACert %q: no certificates parsed","messagePattern":"ExternalDoltConfig: TLSCACert %q: no certificates parsed","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/configfile/external_dolt_config.go","lineNumber":128,"sourceCode":"\n\tif c.TLSSkipVerify {\n\t\tcfg.InsecureSkipVerify = true //nolint:gosec // G402: opt-in insecure transport via the TLSSkipVerify testing flag\n\t} else {\n\t\tname := c.TLSServerName\n\t\tif name == \"\" {\n\t\t\tname = c.Host\n\t\t}\n\t\tcfg.ServerName = name\n\t}\n\n\tif c.TLSCACert != \"\" {\n\t\tpem, err := os.ReadFile(c.TLSCACert)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ExternalDoltConfig: read TLSCACert: %w\", err)\n\t\t}\n\t\tpool := x509.NewCertPool()\n\t\tif !pool.AppendCertsFromPEM(pem) {\n\t\t\treturn nil, fmt.Errorf(\"ExternalDoltConfig: TLSCACert %q: no certificates parsed\", c.TLSCACert)\n\t\t}\n\t\tcfg.RootCAs = pool\n\t}\n\n\tif c.TLSCert != \"\" {\n\t\tcrt, err := tls.LoadX509KeyPair(c.TLSCert, c.TLSKey)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ExternalDoltConfig: load client cert/key: %w\", err)\n\t\t}\n\t\tcfg.Certificates = []tls.Certificate{crt}\n\t}\n\n\treturn cfg, nil\n}\n","sourceCodeStart":110,"sourceCodeEnd":143,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/configfile/external_dolt_config.go#L110-L143","documentation":"TLSClientConfig read the TLSCACert file successfully but x509.AppendCertsFromPEM could not parse any certificates from its contents. The file exists but is not a valid PEM-encoded certificate bundle (or is empty/wrong type). The root CA pool would be empty, so the config is rejected.","triggerScenarios":"Calling registerExternalTLSConfig -> TLSClientConfig where TLSCACert contains DER-encoded bytes, a private key, an empty file, HTML (e.g. an error page saved as ca.pem), or concatenated garbage.","commonSituations":"Downloading a CA cert that saved as DER instead of PEM; accidentally pointing TLSCACert at the server cert's key file; truncated file from a failed transfer; empty placeholder file created by config tooling.","solutions":["Verify the file starts with '-----BEGIN CERTIFICATE-----' (head -1 file); re-export in PEM format if not (openssl x509 -inform DER -in ca.der -out ca.pem).","Check the file is non-empty and not truncated (openssl x509 -in ca.pem -noout -subject).","Make sure TLSCACert points to the CA certificate, not a private key or leaf key.","Re-download/copy the CA bundle from the server operator."],"exampleFix":"// before\ntlsCACert: \"/etc/beads/tls/ca.der\"  // DER encoded\n// after\n# openssl x509 -inform DER -in ca.der -out /etc/beads/tls/ca.pem\ntlsCACert: \"/etc/beads/tls/ca.pem\"","handlingStrategy":"validation","validationCode":"pem, err := os.ReadFile(cfg.TLSCACert)\nif err != nil {\n    return err\n}\nif !x509.NewCertPool().AppendCertsFromPEM(pem) {\n    return fmt.Errorf(\"%s contains no PEM certificates\", cfg.TLSCACert)\n}","typeGuard":"func isPEMCert(path string) bool {\n    b, err := os.ReadFile(path)\n    if err != nil || len(b) == 0 {\n        return false\n    }\n    return x509.NewCertPool().AppendCertsFromPEM(b)\n}","tryCatchPattern":"cfg, err := extCfg.TLSClientConfig()\nif err != nil && strings.Contains(err.Error(), \"no certificates parsed\") {\n    return fmt.Errorf(\"CA file at %s is not PEM; re-export with: openssl x509 -inform DER -in ca.der -out ca.pem\", extCfg.TLSCACert)\n}","preventionTips":["Always export CAs in PEM format (openssl x509, not DER)","Sanity-check downloaded certs with openssl x509 -noout -subject before use","Never point TLSCACert at key files or HTML pages"],"tags":["tls","certificates","pem","parsing"],"backgroundTag":"tls-ca-cert-unreadable","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}