vitessio/vitess · error

failed to generate vtctld consul datacenter from template

Error message

failed to generate vtctld consul datacenter from template: %w

What it means

Analogous to the vtgate case, NewConsul renders the optional vtctld datacenter template via generateConsulDatacenter and wraps any parse/execute failure with this message. It signals that the vtctld datacenter template string is invalid or cannot be rendered against the cluster proto.

Solutions

  1. Fix the vtctld datacenter template syntax to be a valid Go text/template.
  2. Reference only valid vtadminpb.Cluster fields (e.g. {{ .Cluster.Id }}).
  3. Leave the vtctld datacenter template empty to skip the feature — it is only parsed when non-empty.
  4. Read the wrapped inner error to distinguish parse vs execution failure.

Example fix

// before
--vtctld-datacenter-template="{{ if .Cluster.Id }}dc1"
// after
--vtctld-datacenter-template="{{ if .Cluster.Id }}dc1{{ end }}"
Defensive patterns

Strategy: validation

Validate before calling

if s := *vtctldDatacenterTmplStr; s != "" {
    if _, err := template.New("check").Parse(s); err != nil {
        return fmt.Errorf("invalid vtctld dc template: %w", err)
    }
}

Try / catch

disco, err := NewConsul(cluster, args)
if err != nil && strings.Contains(err.Error(), "vtctld consul datacenter") {
    log.Fatalf("bad vtctld datacenter template: %v", err)
}

Prevention

When it happens

Trigger: Passing a non-empty vtctld-datacenter-template flag whose Go template fails to parse (syntax error) or whose execution against &struct{Cluster *vtadminpb.Cluster} fails, during NewConsul.

Common situations: Invalid template syntax from manual edits, referencing nonexistent Cluster fields, environment/config tooling mangling braces, copy-paste between vtgate and vtctld flags introducing a typo.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/93225ff6aa8be0a8. Report an issue: GitHub.

Appendix: source

Thrown at go/vt/vtadmin/cluster/discovery/discovery_consul.go:148

	}

	if *vtgateFQDNTmplStr != "" {
		disco.vtgateFQDNTmpl, err = template.New("consul-vtgate-fqdn-template-" + cluster.Id).Parse(*vtgateFQDNTmplStr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse vtgate FQDN template %s: %w", *vtgateFQDNTmplStr, err)
		}
	}

	disco.vtgateAddrTmpl, err = template.New("consul-vtgate-address-template-" + cluster.Id).Parse(*vtgateAddrTmplStr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse vtgate host address template %s: %w", *vtgateAddrTmplStr, err)
	}

	/* vtctld options */
	if *vtctldDatacenterTmplStr != "" {
		disco.vtctldDatacenter, err = generateConsulDatacenter("vtctld", cluster, *vtctldDatacenterTmplStr)
		if err != nil {
			return nil, fmt.Errorf("failed to generate vtctld consul datacenter from template: %w", err)
		}
	}

	if *vtctldFQDNTmplStr != "" {
		disco.vtctldFQDNTmpl, err = template.New("consul-vtctld-fqdn-template-" + cluster.Id).Parse(*vtctldFQDNTmplStr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse vtctld FQDN template %s: %w", *vtctldFQDNTmplStr, err)
		}
	}

	disco.vtctldAddrTmpl, err = template.New("consul-vtctld-address-template-" + cluster.Id).Parse(*vtctldAddrTmplStr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse vtctld host address template %s: %w", *vtctldAddrTmplStr, err)
	}

	return disco, nil
}

View on GitHub (pinned to 01a25a7d17)