grpc/grpc-go · error

xds: field clientListenerResourceNameTemplate

Error message

xds: field clientListenerResourceNameTemplate %q of authority %q doesn't start with prefix %q

What it means

Returned by Config.UnmarshalJSON when an authority's client_listener_resource_name_template, if set, does not start with the required prefix 'xdstp://<authority_name>/'. The prefix is derived from the authority's map key, URL-path-escaped.

Solutions

  1. Either remove the client_listener_resource_name_template field (it defaults to 'xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s') or set it to start with 'xdstp://<authority_name>/'.
  2. Match the authority key exactly in the prefix, including URL path-escaping for special characters.
  3. Keep the '%s' substitution token so the service authority can be injected.
  4. Use the same spelling/casing for the authority key and the prefix.

Example fix

// before (key 'auth1' but template uses 'auth2')
"authorities":{"auth1":{"client_listener_resource_name_template":"xdstp://auth2/envoy.config.listener.v3.Listener/%s"}}

// after
"authorities":{"auth1":{"client_listener_resource_name_template":"xdstp://auth1/envoy.config.listener.v3.Listener/%s"}}
Defensive patterns

Strategy: validation

Validate before calling

// For each authority, confirm its template prefix matches its key.
func validateAuthorityPrefixes(auths map[string]map[string]string) error {
    for name, a := range auths {
        tmpl := a["client_listener_resource_name_template"]
        if tmpl == "" {
            continue // default is fine
        }
        want := "xdstp://" + url.PathEscape(name) + "/"
        if !strings.HasPrefix(tmpl, want) {
            return fmt.Errorf("authority %q template must start with %q", name, want)
        }
    }
    return nil
}

Try / catch

if _, err := bootstrap.NewConfigFromContents(data); err != nil {
    if strings.Contains(err.Error(), "clientListenerResourceNameTemplate") {
        // align the authority template prefix with the authority key.
    }
}

Prevention

When it happens

Trigger: Triggered at bootstrap.go:644 when authority.ClientListenerResourceNameTemplate is non-empty and does not have the prefix strings.HasPrefix(template, 'xdstp://<name>/').

Common situations: Template copied from another authority without updating the prefix to match the new authority key; template uses an old-style (non-xdstp) name; authority key renamed but template not updated; special characters in the authority name not path-escaped consistently.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/d803e0d8880e1589. Report an issue: GitHub.

Appendix: source

Thrown at internal/xds/bootstrap/bootstrap.go:644

	// Default value of the default client listener name template is "%s".
	if c.clientDefaultListenerResourceNameTemplate == "" {
		c.clientDefaultListenerResourceNameTemplate = "%s"
	}
	if len(c.xDSServers) == 0 {
		return fmt.Errorf("xds: required field `xds_servers` not found in bootstrap configuration: %s", string(data))
	}

	// Post-process the authorities' client listener resource template field:
	// - if set, it must start with "xdstp://<authority_name>/"
	// - if not set, it defaults to "xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s"
	for name, authority := range c.authorities {
		prefix := fmt.Sprintf("xdstp://%s", url.PathEscape(name))
		if authority.ClientListenerResourceNameTemplate == "" {
			authority.ClientListenerResourceNameTemplate = prefix + "/envoy.config.listener.v3.Listener/%s"
			continue
		}
		if !strings.HasPrefix(authority.ClientListenerResourceNameTemplate, prefix) {
			return fmt.Errorf("xds: field clientListenerResourceNameTemplate %q of authority %q doesn't start with prefix %q", authority.ClientListenerResourceNameTemplate, name, prefix)
		}
	}
	return nil
}

// GetConfiguration returns the bootstrap configuration initialized by reading
// the bootstrap file found at ${GRPC_XDS_BOOTSTRAP} or bootstrap contents
// specified at ${GRPC_XDS_BOOTSTRAP_CONFIG}. If both env vars are set, the
// former is preferred.
//
// This function tries to process as much of the bootstrap file as possible (in
// the presence of the errors) and may return a Config object with certain
// fields left unspecified, in which case the caller should use some sane
// defaults.
//
// This function returns an error if it's unable to parse the contents of the
// bootstrap config. It returns (nil, nil) if none of the env vars are set.
func GetConfiguration() (*Config, error) {

View on GitHub (pinned to 0c51461d27)