{"record":{"id":"cceb988559185bfc","repo":"vitessio/vitess","slug":"failed-to-execute-vtctld-fqdn-template-for-v-w","errorCode":null,"errorMessage":"failed to execute vtctld fqdn template for %v: %w","messagePattern":"failed to execute vtctld fqdn template for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/cluster/discovery/discovery_consul.go","lineNumber":437,"sourceCode":"\t\treturn nil, err\n\t}\n\n\tvtctlds := make([]*vtadminpb.Vtctld, len(entries))\n\n\tfor i, entry := range entries {\n\t\tvtctld := &vtadminpb.Vtctld{\n\t\t\tCluster: &vtadminpb.Cluster{\n\t\t\t\tId:   c.cluster.Id,\n\t\t\t\tName: c.cluster.Name,\n\t\t\t},\n\t\t\tHostname: entry.Node.Node,\n\t\t}\n\n\t\tif executeFQDNTemplate {\n\t\t\tif c.vtctldFQDNTmpl != nil {\n\t\t\t\tvtctld.FQDN, err = textutil.ExecuteTemplate(c.vtctldFQDNTmpl, vtctld)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"failed to execute vtctld fqdn template for %v: %w\", vtctld, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvtctlds[i] = vtctld\n\t}\n\n\treturn vtctlds, nil\n}\n\n// getQueryOptions returns a shallow copy so we can swap in the vtgateDatacenter.\n// If we were to set it directly, we'd need a mutex to guard against concurrent\n// vtgate and (soon) vtctld queries.\nfunc (c *ConsulDiscovery) getQueryOptions() consul.QueryOptions {\n\tif c.queryOptions == nil {\n\t\treturn consul.QueryOptions{}\n\t}\n","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/cluster/discovery/discovery_consul.go#L419-L455","documentation":"discoverVtctlds optionally sets each vtctld's FQDN by executing c.vtctldFQDNTmpl (when executeFQDNTemplate is set and the template is non-nil). A template execution error is wrapped and returned, failing the entire vtctld discovery call that produced it.","triggerScenarios":"Calling DiscoverVtctlds / DiscoverVtctldAddrs / discoverVtctld on a cluster with a vtctld fqdn template configured, where executing the template against a vtctld errors out.","commonSituations":"FQDN template referencing consul catalog fields that were never copied onto the vtctld proto; malformed {{...}} syntax; template written for vtgate reused for vtctld with different fields.","solutions":["Fix the vtctld fqdn template to use only vtctld proto fields","Remove the fqdn template from config if FQDN rewriting is unnecessary","Test the template against a sample vtctld struct before rollout"],"exampleFix":"// before\nvtctld_fqdn_template: \"{{.Node}}.{{.Datacenter}}.consul\"\n// after\nvtctld_fqdn_template: \"{{.Hostname}}.example.internal\"","handlingStrategy":"validation","validationCode":"if cfg.VtctldFQDNTmpl != \"\" {\n\tfqdnTmpl := template.Must(template.New(\"vtctld-fqdn\").Parse(cfg.VtctldFQDNTmpl))\n\tvar sample vtadminpb.Vtctld\n\tif err := fqdnTmpl.Execute(io.Discard, &sample); err != nil {\n\t\treturn fmt.Errorf(\"invalid vtctld fqdn template: %w\", err)\n\t}\n}","typeGuard":null,"tryCatchPattern":"vts, err := cluster.DiscoverVtctlds(ctx)\nif err != nil {\n\tif strings.Contains(err.Error(), \"fqdn template\") {\n\t\tlog.Errorf(\"bad vtctld fqdn template: %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Reference only vtctld proto fields in the fqdn template","Remove fqdn templating when FQDN rewriting is unnecessary","Test fqdn rendering with a single vtctld first","Re-check templates after Vitess upgrades"],"tags":["go","vtadmin","consul","template","vtctld","fqdn"],"backgroundTag":"template-execution-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}