gastownhall/beads · error

ExternalDoltConfig: TLSCACert %q: no certificates parsed

Error message

ExternalDoltConfig: TLSCACert %q: no certificates parsed

What it means

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.

Source

Thrown at internal/configfile/external_dolt_config.go:128

	if c.TLSSkipVerify {
		cfg.InsecureSkipVerify = true //nolint:gosec // G402: opt-in insecure transport via the TLSSkipVerify testing flag
	} else {
		name := c.TLSServerName
		if name == "" {
			name = c.Host
		}
		cfg.ServerName = name
	}

	if c.TLSCACert != "" {
		pem, err := os.ReadFile(c.TLSCACert)
		if err != nil {
			return nil, fmt.Errorf("ExternalDoltConfig: read TLSCACert: %w", err)
		}
		pool := x509.NewCertPool()
		if !pool.AppendCertsFromPEM(pem) {
			return nil, fmt.Errorf("ExternalDoltConfig: TLSCACert %q: no certificates parsed", c.TLSCACert)
		}
		cfg.RootCAs = pool
	}

	if c.TLSCert != "" {
		crt, err := tls.LoadX509KeyPair(c.TLSCert, c.TLSKey)
		if err != nil {
			return nil, fmt.Errorf("ExternalDoltConfig: load client cert/key: %w", err)
		}
		cfg.Certificates = []tls.Certificate{crt}
	}

	return cfg, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. 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).
  2. Check the file is non-empty and not truncated (openssl x509 -in ca.pem -noout -subject).
  3. Make sure TLSCACert points to the CA certificate, not a private key or leaf key.
  4. Re-download/copy the CA bundle from the server operator.

Example fix

// before
tlsCACert: "/etc/beads/tls/ca.der"  // DER encoded
// after
# openssl x509 -inform DER -in ca.der -out /etc/beads/tls/ca.pem
tlsCACert: "/etc/beads/tls/ca.pem"
Defensive patterns

Strategy: validation

Validate before calling

pem, err := os.ReadFile(cfg.TLSCACert)
if err != nil {
    return err
}
if !x509.NewCertPool().AppendCertsFromPEM(pem) {
    return fmt.Errorf("%s contains no PEM certificates", cfg.TLSCACert)
}

Type guard

func isPEMCert(path string) bool {
    b, err := os.ReadFile(path)
    if err != nil || len(b) == 0 {
        return false
    }
    return x509.NewCertPool().AppendCertsFromPEM(b)
}

Try / catch

cfg, err := extCfg.TLSClientConfig()
if err != nil && strings.Contains(err.Error(), "no certificates parsed") {
    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)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/3945fd7abc81f27f. Report an issue: GitHub.