{"record":{"id":"1866a82f7216b789","repo":"vitessio/vitess","slug":"failed-to-execute-vtctld-address-template-for-v","errorCode":null,"errorMessage":"failed to execute vtctld address template for %v: %w","messagePattern":"failed to execute vtctld address template for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtadmin/cluster/discovery/discovery_consul.go","lineNumber":369,"sourceCode":"\n\treturn vtctlds[rand.IntN(len(vtctlds))], nil\n}\n\n// DiscoverVtctldAddr is part of the Discovery interface.\nfunc (c *ConsulDiscovery) DiscoverVtctldAddr(ctx context.Context, tags []string) (string, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"ConsulDiscovery.DiscoverVtctldAddr\")\n\tdefer span.Finish()\n\n\texecuteFQDNTemplate := false\n\n\tvtctld, err := c.discoverVtctld(ctx, tags, executeFQDNTemplate)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\taddr, err := textutil.ExecuteTemplate(c.vtctldAddrTmpl, vtctld)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to execute vtctld address template for %v: %w\", vtctld, err)\n\t}\n\n\treturn addr, nil\n}\n\n// DiscoverVtctldAddrs is part of the Discovery interface.\nfunc (c *ConsulDiscovery) DiscoverVtctldAddrs(ctx context.Context, tags []string) ([]string, error) {\n\tspan, ctx := trace.NewSpan(ctx, \"ConsulDiscovery.DiscoverVtctldAddrs\")\n\tdefer span.Finish()\n\n\texecuteFQDNTemplate := false\n\n\tvtctlds, err := c.discoverVtctlds(ctx, tags, executeFQDNTemplate)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\taddrs := make([]string, len(vtctlds))","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtadmin/cluster/discovery/discovery_consul.go#L351-L387","documentation":"Cluster.DiscoverVtctldAddr renders the vtctld address from c.vtctldAddrTmpl via textutil.ExecuteTemplate. When template execution fails (invalid field access or malformed action against the vtctld protobuf), the error is wrapped with the vtctld value and returned to the caller.","triggerScenarios":"Calling DiscoverVtctldAddr when the vtctld address template in the cluster config references fields that do not exist on the vtctld proto message or has invalid template syntax.","commonSituations":"Config copied from a vtgate template but applied to vtctld whose proto differs; typo in field name; template expecting a port field that was renamed.","solutions":["Correct the vtctld address template so its actions match the vtctld proto fields","Compile-check the template with template.Must at startup so bad templates fail fast","Render the template against a sample vtctld struct locally to confirm the output"],"exampleFix":"// before\nvtctld_addr_template: \"{{.GrpcAddress}}\"\n// after\nvtctld_addr_template: \"{{.Hostname}}:{{.PortStr}}\"","handlingStrategy":"validation","validationCode":"vtctldTmpl := template.Must(template.New(\"vtctld-addr\").Parse(cfg.VtctldAddrTmpl))\nvar sample vtadminpb.Vtctld\nif err := vtctldTmpl.Execute(io.Discard, &sample); err != nil {\n\treturn fmt.Errorf(\"invalid vtctld address template: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"addr, err := cluster.DiscoverVtctldAddr(ctx)\nif err != nil {\n\tlog.Errorf(\"vtctld addr template failed: %v\", err)\n\treturn defaultVtctldAddr, nil\n}","preventionTips":["Use vtctld-specific templates, not vtgate templates copied over","Compile templates with template.Must at startup","Render templates against sample structs in unit tests","Check vtctld proto fields after each Vitess upgrade"],"tags":["go","vtadmin","consul","template","vtctld"],"backgroundTag":"template-execution-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}