{"record":{"id":"d803e0d8880e1589","repo":"grpc/grpc-go","slug":"xds-field-clientlistenerresourcenametemplate-q-o","errorCode":null,"errorMessage":"xds: field clientListenerResourceNameTemplate %q of authority %q doesn't start with prefix %q","messagePattern":"xds: field clientListenerResourceNameTemplate %q of authority %q doesn't start with prefix %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/bootstrap/bootstrap.go","lineNumber":644,"sourceCode":"\t// Default value of the default client listener name template is \"%s\".\n\tif c.clientDefaultListenerResourceNameTemplate == \"\" {\n\t\tc.clientDefaultListenerResourceNameTemplate = \"%s\"\n\t}\n\tif len(c.xDSServers) == 0 {\n\t\treturn fmt.Errorf(\"xds: required field `xds_servers` not found in bootstrap configuration: %s\", string(data))\n\t}\n\n\t// Post-process the authorities' client listener resource template field:\n\t// - if set, it must start with \"xdstp://<authority_name>/\"\n\t// - if not set, it defaults to \"xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s\"\n\tfor name, authority := range c.authorities {\n\t\tprefix := fmt.Sprintf(\"xdstp://%s\", url.PathEscape(name))\n\t\tif authority.ClientListenerResourceNameTemplate == \"\" {\n\t\t\tauthority.ClientListenerResourceNameTemplate = prefix + \"/envoy.config.listener.v3.Listener/%s\"\n\t\t\tcontinue\n\t\t}\n\t\tif !strings.HasPrefix(authority.ClientListenerResourceNameTemplate, prefix) {\n\t\t\treturn fmt.Errorf(\"xds: field clientListenerResourceNameTemplate %q of authority %q doesn't start with prefix %q\", authority.ClientListenerResourceNameTemplate, name, prefix)\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetConfiguration returns the bootstrap configuration initialized by reading\n// the bootstrap file found at ${GRPC_XDS_BOOTSTRAP} or bootstrap contents\n// specified at ${GRPC_XDS_BOOTSTRAP_CONFIG}. If both env vars are set, the\n// former is preferred.\n//\n// This function tries to process as much of the bootstrap file as possible (in\n// the presence of the errors) and may return a Config object with certain\n// fields left unspecified, in which case the caller should use some sane\n// defaults.\n//\n// This function returns an error if it's unable to parse the contents of the\n// bootstrap config. It returns (nil, nil) if none of the env vars are set.\nfunc GetConfiguration() (*Config, error) {","sourceCodeStart":626,"sourceCodeEnd":662,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/bootstrap/bootstrap.go#L626-L662","documentation":"For each authority in the authorities map the library validates that, when client_listener_resource_name_template is explicitly set, it must start with xdstp://<authority_name> (URL-path-escaped). This enforces the gRFC rule that an authority-scoped listener template is namespaced under that authority. A mismatch is treated as a bootstrap parse error.","triggerScenarios":"An authority whose clientListenerResourceNameTemplate was set to a plain name or an xdstp URI with a different authority host. For authority \"foo\", the template must begin with xdstp://foo/; anything else triggers this error.","commonSituations":"Copy-pasting a listener template from another authority without updating the host portion; using a legacy grpc:// or plain string template; URL-encoding the authority name inconsistently between the key and the prefix.","solutions":["Set the template to start with xdstp://<authority_name>/ where <authority_name> exactly matches the map key (the library uses url.PathEscape on the key for comparison).","If you do not need a custom template, omit client_listener_resource_name_template so it defaults to xdstp://<authority_name>/envoy.config.listener.v3.Listener/%s.","Ensure the %s placeholder is preserved after the prefix if you use a custom resource path."],"exampleFix":"// before — authority key is \"my-authority\" but template uses wrong host:\n//   \"authorities\": {\n//     \"my-authority\": { \"client_listener_resource_name_template\": \"xdstp://other-auth/Listener/%s\" }\n//   }\n// after:\n//   \"authorities\": {\n//     \"my-authority\": { \"client_listener_resource_name_template\": \"xdstp://my-authority/Listener/%s\" }\n//   }","handlingStrategy":"validation","validationCode":"// Validate authority templates match their key prefix.\nfunc validateAuthorityTemplates(authorities map[string]json.RawMessage) error {\n    for name, raw := range authorities {\n        var a struct{ Tmpl string `json:\"client_listener_resource_name_template\"` }\n        _ = json.Unmarshal(raw, &a)\n        if a.Tmpl == \"\" { continue } // default is fine\n        prefix := \"xdstp://\" + url.PathEscape(name)\n        if !strings.HasPrefix(a.Tmpl, prefix) {\n            return fmt.Errorf(\"authority %q template %q must start with %q\", name, a.Tmpl, prefix)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Omit client_listener_resource_name_template unless you need a custom path, so the default applies.","When copy-pasting an authority entry, always update the xdstp:// host to match the key.","Use url.PathEscape on the authority name when constructing the prefix to match the library's comparison."],"tags":["xds","bootstrap","authority","xdstp","config","grpc"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}