gastownhall/beads · error

ExternalDoltConfig: TLSCert/TLSKey set without TLSRequired

Error message

ExternalDoltConfig: TLSCert/TLSKey set without TLSRequired

What it means

ExternalDoltConfig.Validate() enforces that TLS material is only supplied when TLSRequired=true. TLSCert and/or TLSKey are client certificate files for mutual TLS; setting them while TLSRequired is false means the config asks for a client cert but then builds no TLS connection at all (TLSClientConfig returns nil when !TLSRequired), so the fields would be silently ignored. This error makes that contradiction a hard startup failure instead.

Source

Thrown at internal/configfile/external_dolt_config.go:85

		return errors.New("ExternalDoltConfig: TLSKey set without TLSCert")
	}

	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
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set TLSRequired: true in the config if the external Dolt server actually requires TLS/mTLS.
  2. Remove the TLSCert and TLSKey fields if the server really is plaintext-only.
  3. Verify the server's actual TLS requirement (does it listen with --tls?) and align the config before restarting.

Example fix

// before (YAML)
external:
  host: dolt.internal
  port: 3307
  tls_cert: /etc/beads/client-cert.pem
  tls_key: /etc/beads/client-key.pem
// after
external:
  host: dolt.internal
  port: 3307
  tls_required: true
  tls_cert: /etc/beads/client-cert.pem
  tls_key: /etc/beads/client-key.pem
Defensive patterns

Strategy: validation

Validate before calling

func validateTLS(cfg configfile.ExternalDoltConfig) error {
	if (cfg.TLSCert != "" || cfg.TLSKey != "") && !cfg.TLSRequired {
		return fmt.Errorf("tls_cert/tls_key require tls_required: true")
	}
	return cfg.Validate()
}
// call before handing the config to NewExternalDoltServer / NewExternalDoltServerUOWProvider

Try / catch

if err := configfile.ExternalDoltConfig{...}.Validate(); err != nil {
	if strings.Contains(err.Error(), "TLSCert/TLSKey set without TLSRequired") {
		// fix config: enable tls_required or drop cert/key, then retry
	}
	return err
}

Prevention

When it happens

Trigger: Any of: constructing ExternalDoltConfig with TLSCert or TLSKey set but TLSRequired omitted/false, then calling Validate() directly or indirectly via buildProxiedServerClientInfo, NewExternalDoltServer, or NewExternalDoltServerUOWProvider. Typically a YAML/env config that sets tls_cert/tls_key (copied from a mTLS setup) but forgets tls_required: true.

Common situations: Copying a working mTLS client config from another service but omitting the enable-TLS flag; toggling TLSRequired off during debugging while leaving cert/key paths in place; template/tooling that renders tls_cert and tls_key unconditionally.

Related errors


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