grpc/grpc-go · error

xDS TLS credentials only support one mode

Error message

xDS TLS credentials only support one mode

What it means

The xDS TLS credentials bundle is single-mode: it always uses TLS transport credentials and does not support alternate modes. bundle.NewWithMode (bundle.go:108-111) unconditionally returns this error. Calling NewWithMode is a programming error — there is no input that makes it succeed.

Source

Thrown at internal/xds/bootstrap/tlscreds/bundle.go:111

	return &bundle{
		transportCredentials: &reloadingCreds{provider: provider},
	}, sync.OnceFunc(func() { provider.Close() }), nil
}

func (t *bundle) TransportCredentials() credentials.TransportCredentials {
	return t.transportCredentials
}

func (t *bundle) PerRPCCredentials() credentials.PerRPCCredentials {
	// mTLS provides transport credentials only. There are no per-RPC
	// credentials.
	return nil
}

func (t *bundle) NewWithMode(string) (credentials.Bundle, error) {
	// This bundle has a single mode which only uses TLS transport credentials,
	// so there is no legitimate case where callers would call NewWithMode.
	return nil, fmt.Errorf("xDS TLS credentials only support one mode")
}

// reloadingCreds is a credentials.TransportCredentials for client
// side mTLS that reloads the server root CA certificate and the client
// certificates from the provider on every client handshake. This is necessary
// because the standard TLS credentials do not support reloading CA
// certificates.
type reloadingCreds struct {
	provider certprovider.Provider
}

func (c *reloadingCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
	km, err := c.provider.KeyMaterial(ctx)
	if err != nil {
		return nil, nil, err
	}
	var config *tls.Config
	if km.SPIFFEBundleMap != nil {

View on GitHub (pinned to 03255a9237)

Solutions

  1. Do not call NewWithMode on the xDS TLS bundle — it is intentionally unsupported.
  2. Restructure calling code to skip NewWithMode for bundles that only have one mode, or guard with a type assertion.
  3. Use TransportCredentials() directly to obtain the TLS credentials.

Example fix

// before:
//   newBundle, err := bdl.NewWithMode("mtls")
// after:
//   tc := bdl.TransportCredentials()
//   // use tc directly; do not call NewWithMode on xDS TLS bundles
Defensive patterns

Strategy: type-guard

Type guard

// Never call NewWithMode on the xDS TLS bundle.
func supportsModes(b credentials.Bundle) bool {
    // The xDS TLS bundle does not support modes; treat it as single-mode.
    return false
}

Try / catch

// Guard generic code that iterates modes.
if _, err := bdl.NewWithMode(name); err != nil {
    if strings.Contains(err.Error(), "only support one mode") {
        // expected for xDS TLS bundle; use TransportCredentials() instead
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: User code or an integration calls NewWithMode on a bundle returned by tlscreds.NewBundle. The credentials.Bundle interface includes NewWithMode, but this implementation rejects all calls because mTLS credentials have only one mode.

Common situations: Generic code that calls NewWithMode on every credentials.Bundle as part of a mode-switching routine; porting a custom credentials bundle to xDS where mode switching was expected; a framework that probes NewWithMode during setup.

Understand the failure class

Related errors


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