vitessio/vitess · critical

failed to parse tablet fqdn template %s: %w

Error message

failed to parse tablet fqdn template %s: %w

What it means

cluster.New fails when the optional --tablet-fqdn-template value cannot be parsed as a Go text/template for the cluster. This is a pure config-syntax error: the template string (e.g. used to render tablet FQDNs) is invalid template syntax or references bad actions.

Source

Thrown at go/vt/vtadmin/cluster/cluster.go:153

	for _, opt := range cfg.vtctldConfigOpts {
		vtctldCfg = opt(vtctldCfg)
	}

	cluster.DB, err = vtsql.New(ctx, vtsqlCfg)
	if err != nil {
		return nil, fmt.Errorf("error creating vtsql proxy: %w", err)
	}

	cluster.Vtctld, err = vtctldclient.New(ctx, vtctldCfg)
	if err != nil {
		return nil, fmt.Errorf("error creating vtctldclient: %w", err)
	}

	if cfg.TabletFQDNTmplStr != "" {
		cluster.TabletFQDNTmpl, err = template.New(cluster.ID + "-tablet-fqdn").Parse(cfg.TabletFQDNTmplStr)
		if err != nil {
			return nil, fmt.Errorf("failed to parse tablet fqdn template %s: %w", cfg.TabletFQDNTmplStr, err)
		}
	}

	cluster.backupReadPool = cfg.BackupReadPoolConfig.NewReadPool()
	cluster.schemaReadPool = cfg.SchemaReadPoolConfig.NewReadPool()
	cluster.topoRWPool = cfg.TopoRWPoolConfig.NewRWPool()
	cluster.topoReadPool = cfg.TopoReadPoolConfig.NewReadPool()
	cluster.workflowReadPool = cfg.WorkflowReadPoolConfig.NewReadPool()

	cluster.emergencyFailoverPool = cfg.EmergencyFailoverPoolConfig.NewRWPool()
	cluster.failoverPool = cfg.FailoverPoolConfig.NewRWPool()

	if cluster.cfg.SchemaCacheConfig == nil {
		cluster.cfg.SchemaCacheConfig = &cache.Config{}
	}
	cluster.schemaCache = cache.New(func(ctx context.Context, key schemacache.Key) ([]*vtadminpb.Schema, error) {
		// TODO: make a private method to separate the fetching bits from the cache bits
		if key.Keyspace == "" {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Fix the template syntax in --tablet-fqdn-template (valid Go text/template, e.g. '{{.Tablet.Alias}}.{{.Cell}}.example.com')
  2. Validate the template with a small Go snippet or text/template playground before deploying
  3. Remove the flag if templating is not needed

Example fix

// before
--tablet-fqdn-template '{{.Alias' 
// after
--tablet-fqdn-template '{{.Tablet.Alias}}.{{.Cell}}.ks.example.com'
Defensive patterns

Strategy: validation

Validate before calling

// validate template before passing to config
if cfg.TabletFQDNTmplStr != "" {
    if _, err := texttemplate.New("check").Parse(cfg.TabletFQDNTmplStr); err != nil {
        return fmt.Errorf("invalid --tablet-fqdn-template: %w", err)
    }
}

Try / catch

c, err := cluster.BuildCluster(ctx, cfg)
if err != nil {
    if strings.Contains(err.Error(), "tablet fqdn template") {
        return fmt.Errorf("bad --tablet-fqdn-template %q: %w", cfg.TabletFQDNTmplStr, err)
    }
    return err
}

Prevention

When it happens

Trigger: cfg.TabletFQDNTmplStr is non-empty and template.New(...).Parse fails — e.g. unbalanced {{ }}, unknown template functions.

Common situations: Typo in the template such as '{{.Alias}}' vs '{{ .Alias }}' mistakes like '{{.Alias'; copy-paste artifacts; using unsupported fields/functions.

Understand the failure class

Related errors


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