gastownhall/beads · error

register TLS config: %w

Error message

register TLS config: %w

What it means

registerExternalTLSConfig fails with this when the go-sql-driver mysql package rejects the TLS config registration via mysql.RegisterTLSConfig. In practice this happens when the config name "beads-external-<serverID>" is already registered with a different *tls.Config, or the supplied config is invalid per the driver's rules (nil key data etc.). The name is derived from the external server ID, so the same server config re-registered with changed TLS material collides.

Source

Thrown at internal/storage/uow/external_doltserver_provider.go:83

	})
	if err != nil {
		return nil, fmt.Errorf("uow: get proxy endpoint: %w", err)
	}

	return openAndInitSchema(ctx, ep, database, rootUser, rootPassword, tlsConfigName, teamServer, expectedProjectID, applyProviderOptions(opts))
}

func registerExternalTLSConfig(external configfile.ExternalDoltConfig) (string, error) {
	if !external.TLSRequired {
		return "", nil
	}
	tc, err := external.TLSClientConfig()
	if err != nil {
		return "", err
	}
	name := "beads-external-" + server.ExternalDoltServerID(external)
	if err := mysql.RegisterTLSConfig(name, tc); err != nil {
		return "", fmt.Errorf("register TLS config: %w", err)
	}
	return name, nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Reuse one provider instance per server instead of reconstructing it repeatedly in the same process.
  2. Call mysql.DeregisterTLSConfig("beads-external-<serverID>") before re-registering when the TLS material legitimately changed.
  3. If the material didn't change, treat registration as idempotent in your wrapper and ignore/reuse the existing config name.
  4. Check whether two different configs accidentally produce the same server ID (same host/port/socket fields).

Example fix

// before
name := "beads-external-" + server.ExternalDoltServerID(external)
if err := mysql.RegisterTLSConfig(name, tc); err != nil {
	return "", fmt.Errorf("register TLS config: %w", err)
}
// after
name := "beads-external-" + server.ExternalDoltServerID(external)
if _, exists := mysql.TLSConfigMap[name]; !exists {
	if err := mysql.RegisterTLSConfig(name, tc); err != nil {
		return "", fmt.Errorf("register TLS config: %w", err)
	}
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check for an existing registration before re-creating the provider
if _, exists := mysql.TLSConfigMap["beads-external-"+server.ExternalDoltServerID(external)]; exists {
	mysql.DeregisterTLSConfig("beads-external-" + server.ExternalDoltServerID(external))
}

Try / catch

if err := registerProvider(); err != nil {
	if strings.Contains(err.Error(), "register TLS config:") {
		// same-name collision: deregister and retry once
		mysql.DeregisterTLSConfig("beads-external-" + server.ExternalDoltServerID(external))
		return registerProvider()
	}
	return err
}

Prevention

When it happens

Trigger: Calling NewExternalDoltServerUOWProvider twice in one process with TLSRequired=true for the same external server but a changed tls.Config (e.g. different CA/cert after rotation, or tests constructing providers repeatedly), so mysql.RegisterTLSConfig("beads-external-<id>", tc) returns an error.

Common situations: Test suites or long-lived daemons that recreate providers per operation; TLS cert rotation changing the config while the driver retains the old registration; two ExternalDoltConfigs hashing to the same server ID.

Understand the failure class

Related errors


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