grpc/grpc-go · critical

xds: `server_uri` field in server config cannot be empty: %s

Error message

xds: `server_uri` field in server config cannot be empty: %s

What it means

Returned by ServerConfig.UnmarshalJSON (internal/xds/bootstrap/bootstrap.go:399) when, after JSON parsing, the `server_uri` field is empty. The server URI is the address of the xDS management server and is mandatory; an empty value makes the server config unusable.

Source

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

		for _, cfg := range server.CallCredsConfigs {
			c := bootstrap.GetCallCredentials(cfg.Type)
			if c == nil {
				// Skip unsupported call credential types (don't fail bootstrap).
				continue
			}
			callCreds, cancel, err := c.Build(cfg.Config)
			if err != nil {
				// Call credential validation failed - this should fail bootstrap.
				return fmt.Errorf("failed to build call credentials from bootstrap for %q: %v", cfg.Type, err)
			}
			sc.selectedCallCreds = append(sc.selectedCallCreds, callCreds)
			sc.extraDialOptions = append(sc.extraDialOptions, grpc.WithPerRPCCredentials(callCreds))
			sc.cleanups = append(sc.cleanups, cancel)
		}
	}

	if sc.serverURI == "" {
		return fmt.Errorf("xds: `server_uri` field in server config cannot be empty: %s", string(data))
	}
	if sc.credsDialOption == nil {
		return fmt.Errorf("xds: `channel_creds` field in server config cannot be empty: %s", string(data))
	}
	return nil
}

// ServerConfigTestingOptions specifies options for creating a new ServerConfig
// for testing purposes.
//
// # Testing-Only
type ServerConfigTestingOptions struct {
	// URI is the name of the server corresponding to this server config.
	URI string
	// ChannelCreds contains a list of channel credentials to use when talking
	// to this server. If unspecified, `insecure` credentials will be used.
	ChannelCreds []ChannelCreds
	// CallCredsConfigs contains a list of call credentials to use for individual RPCs

View on GitHub (pinned to 03255a9237)

Solutions

  1. Add a non-empty `server_uri` to the offending server object (e.g. "trafficdirector.googleapis.com:443")
  2. Re-check the bootstrap template/secret injection that populates the URI
  3. Validate the bootstrap JSON for non-empty server_uri before use

Example fix

// before
{"channel_creds":[{"type":"insecure"}]}
// after
{"server_uri":"trafficdirector.googleapis.com:443","channel_creds":[{"type":"google_default"}]}
Defensive patterns

Strategy: validation

Validate before calling

var probe struct {
    XDSServers []struct{ ServerURI string `json:"server_uri"` } `json:"xds_servers"`
}
_ = json.Unmarshal(data, &probe)
for i, s := range probe.XDSServers {
    if strings.TrimSpace(s.ServerURI) == "" {
        return fmt.Errorf("xds_servers[%d] has empty server_uri", i)
    }
}

Try / catch

cfg, err := bootstrap.NewConfigFromContents(data)
if err != nil {
    log.Fatalf("xDS bootstrap invalid (check server_uri): %v", err)
}

Prevention

When it happens

Trigger: A server object in `xds_servers` omits `server_uri` (or sets it to ""), so after unmarshalling sc.serverURI is empty and the post-parse check fails.

Common situations: Hand-edited bootstrap file missing the field; templating that produced an empty URI; copy-paste that dropped server_uri when adapting an example.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/c5291cdad65e4fdf. Report an issue: GitHub.