vitessio/vitess · error

failed to parse vtgate FQDN template %s: %w

Error message

failed to parse vtgate FQDN template %s: %w

What it means

During NewConsul, the vtgate FQDN template string is compiled with template.New(...).Parse. If parsing fails, the raw template string and underlying parse error are wrapped in this message. It indicates the vtgate fully-qualified-domain-name template is syntactically invalid Go template syntax.

Source

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

		"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)
		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)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Fix the template syntax — ensure all {{ }} actions are closed and functions exist.
  2. Verify referenced fields exist on vtadminpb.Cluster (e.g. {{ .Cluster.Id }}).
  3. Empty the vtgate FQDN template flag to skip this option entirely.
  4. Check the wrapped parse error message, which includes the template parse position.

Example fix

// before
vtgate_fqdn_template: "{{ .Cluster.Id }-vtgate.example.com"
// after
vtgate_fqdn_template: "{{ .Cluster.Id }}-vtgate.example.com"
Defensive patterns

Strategy: validation

Validate before calling

if s := *vtgateFQDNTmplStr; s != "" {
    if _, err := template.New("check").Parse(s); err != nil {
        return fmt.Errorf("invalid vtgate FQDN template %q: %w", s, err)
    }
}

Try / catch

disco, err := NewConsul(cluster, args)
if err != nil && strings.Contains(err.Error(), "vtgate FQDN template") {
    log.Fatalf("fix vtgate FQDN template syntax: %v", err)
}

Prevention

When it happens

Trigger: Calling NewConsul (via discovery.New("consul", ...)) with a non-empty vtgate FQDN template flag containing invalid Go template syntax, e.g. "{{ .Cluster.Id" or an unknown pipeline function.

Common situations: Hand-edited YAML/env config with braces stripped or mangled by shell interpolation, copy-paste errors from docs, using fields not present on the Cluster proto.

Understand the failure class

Related errors


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