vitessio/vitess · error

failed to generate vtgate consul datacenter from template: %

Error message

failed to generate vtgate consul datacenter from template: %w

What it means

NewConsul builds the consul discovery implementation and renders optional vtgate datacenter templates against the cluster proto. When generateConsulDatacenter (parse or execute) fails for the vtgate datacenter template, the error is wrapped with this message. It means the vtgate datacenter Go template string itself could not be parsed or rendered with the cluster data.

Source

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

		"Go template string to produce a dialable address from a *vtadminpb.Vtctld "+
			"NOTE: the .FQDN field will never be set in the addr template context.")
	vtctldDatacenterTmplStr := flags.String("vtctld-datacenter-tmpl", "",
		"Go template string to generate the datacenter for vtgate consul queries. "+
			"The cluster name is provided to the template via {{ .Cluster }}. "+
			"Used once during initialization.")
	vtctldFQDNTmplStr := flags.String("vtctld-fqdn-tmpl", "",
		"Optional Go template string to produce an FQDN to access the vtctld from a browser. "+
			"E.g. \"{{ .Hostname }}.example.com\".")

	if err := flags.Parse(args); err != nil {
		return nil, err
	}

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

	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)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Fix the vtgate datacenter template syntax so it is a valid Go text/template (balanced {{ }}, valid field paths).
  2. Reference only fields that exist on vtadminpb.Cluster (e.g. {{ .Cluster.Id }}); inspect the proto for available fields.
  3. Clear the vtgate datacenter template flag if the option is not needed — it is only processed when non-empty.
  4. Inspect the wrapped %w error, which pinpoints parse vs execute failure and the offending offset.

Example fix

// before
--vtgate-datacenter-template="{{ .Cluster.Id"  // unclosed action
// after
--vtgate-datacenter-template="{{ .Cluster.Id }}"
Defensive patterns

Strategy: validation

Validate before calling

tmpl := os.Getenv("VTGATE_DC_TEMPLATE")
if tmpl != "" {
    if _, err := template.New("check").Parse(tmpl); err != nil {
        return fmt.Errorf("invalid vtgate dc template: %w", err)
    }
}

Try / catch

disco, err := NewConsul(cluster, args)
if err != nil {
    var perr *template.Template
    if strings.Contains(err.Error(), "vtgate consul datacenter") {
        log.Fatalf("bad vtgate datacenter template: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Passing a non-empty vtgate-datacenter-template flag value whose template fails to parse (bad Go template syntax, unknown function) or fails to execute (e.g. field access on nil/missing data) during NewConsul.

Common situations: Malformed template like "{{ .Cluster.Names }" (unclosed action), referencing a field that does not exist on vtadminpb.Cluster, or quoting/escaping issues in YAML/env config mangling the template.

Related errors


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