gastownhall/beads · error

ExternalDoltConfig: TLSServerName set without TLSRequired

Error message

ExternalDoltConfig: TLSServerName set without TLSRequired

What it means

Validate() requires that TLSServerName is only set when TLSRequired=true. TLSServerName overrides the SNI/hostname used for certificate verification; without TLSRequired the connection is plaintext and TLSClientConfig() returns nil, so TLSServerName would have no effect. The validator rejects the inert field rather than silently dropping it.

Source

Thrown at internal/configfile/external_dolt_config.go:87

	if c.TLSCert != "" && !filepath.IsAbs(c.TLSCert) {
		return fmt.Errorf("ExternalDoltConfig: TLSCert %q is not absolute", c.TLSCert)
	}
	if c.TLSKey != "" && !filepath.IsAbs(c.TLSKey) {
		return fmt.Errorf("ExternalDoltConfig: TLSKey %q is not absolute", c.TLSKey)
	}
	if c.TLSCACert != "" && !filepath.IsAbs(c.TLSCACert) {
		return fmt.Errorf("ExternalDoltConfig: TLSCACert %q is not absolute", c.TLSCACert)
	}

	if !c.TLSRequired {
		switch {
		case c.TLSCACert != "":
			return errors.New("ExternalDoltConfig: TLSCACert set without TLSRequired")
		case c.TLSCert != "" || c.TLSKey != "":
			return errors.New("ExternalDoltConfig: TLSCert/TLSKey set without TLSRequired")
		case c.TLSServerName != "":
			return errors.New("ExternalDoltConfig: TLSServerName set without TLSRequired")
		case c.TLSSkipVerify:
			return errors.New("ExternalDoltConfig: TLSSkipVerify set without TLSRequired")
		}
	}

	if c.TLSRequired && hasSocket && c.TLSServerName == "" && !c.TLSSkipVerify {
		return errors.New("ExternalDoltConfig: TLSRequired over Socket needs TLSServerName or TLSSkipVerify")
	}

	if c.KeepAlivePeriod < 0 {
		return fmt.Errorf("ExternalDoltConfig: KeepAlivePeriod %s is negative", c.KeepAlivePeriod)
	}

	return nil
}

func (c ExternalDoltConfig) TLSClientConfig() (*tls.Config, error) {
	if !c.TLSRequired {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set TLSRequired: true so the SNI/ServerName override is actually applied.
  2. Delete the TLSServerName field if the connection is intentionally plaintext.
  3. Confirm the endpoint is actually serving TLS and re-enable the full TLS block together (tls_required + ca_cert/server_name).

Example fix

// before
ExternalDoltConfig{Host: "db", Port: 3307, TLSServerName: "dolt.prod.internal"}
// after
ExternalDoltConfig{Host: "db", Port: 3307, TLSRequired: true, TLSServerName: "dolt.prod.internal", TLSCACert: "/etc/beads/ca.pem"}
Defensive patterns

Strategy: validation

Validate before calling

func validateServerName(cfg configfile.ExternalDoltConfig) error {
	if cfg.TLSServerName != "" && !cfg.TLSRequired {
		return fmt.Errorf("tls_server_name requires tls_required: true")
	}
	return cfg.Validate()
}

Try / catch

if err := cfg.Validate(); err != nil {
	if strings.Contains(err.Error(), "TLSServerName set without TLSRequired") {
		// enable tls_required or remove tls_server_name, then retry
	}
	return err
}

Prevention

When it happens

Trigger: Constructing ExternalDoltConfig with TLSServerName set (e.g. tls_server_name in the generated YAML) but TLSRequired false, then calling Validate() directly or via buildProxiedServerClientInfo / NewExternalDoltServer / NewExternalDoltServerUOWProvider.

Common situations: Connecting through a proxy/load-balancer where the cert's CN differs from the dial host, so the operator adds tls_server_name but forgets to enable tls_required; leftover server-name setting after TLS was disabled during incident debugging.

Related errors


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