grpc/grpc-go · error
xDS TLS credentials only support one mode
Error message
xDS TLS credentials only support one mode
What it means
Returned by NewWithMode on the xDS TLS credentials bundle (bundle.go:111). The bundle intentionally implements a single TLS mode and never supports mode switching, as noted in gRFC A65. Any call to NewWithMode is treated as a programming error because the bundle's transport credentials are fixed at construction time.
Solutions
- Do not call NewWithMode on the tlscreds bundle; build a fresh bundle via NewBundle(configJSON) for each desired configuration.
- If you need two credential configurations, keep two separate *bundle instances and select between them at the call site.
- Audit any code path that treats credentials.Bundle polymorphically and special-case or skip NewWithMode for this bundle.
Example fix
// before
b, _, _ := tlscreds.NewBundle(cfg)
newB, err := b.NewWithMode("mtls") // always errors
// after
b, _, _ := tlscreds.NewBundle(cfg) // already the only mode
// use b.TransportCredentials() directly Defensive patterns
Strategy: validation
Validate before calling
// NewWithMode is unsupported; never call it on a tlscreds bundle.
// If you hold a credentials.Bundle and must branch:
if _, ok := b.(*tlscreds.Bundle); ok { /* skip NewWithMode */ } Prevention
- Treat the tlscreds bundle as single-mode; do not write code that calls NewWithMode generically.
- When accepting a credentials.Bundle from callers, document that NewWithMode may be unsupported.
- Add a lint test that greps for NewWithMode usages on bundles returned by tlscreds.NewBundle.
When it happens
Trigger: Calling bundle.NewWithMode("some-mode") on the bundle returned by tlscreds.NewBundle. The method body is unconditional: it always returns this error.
Common situations: Code that generically iterates credentials.Bundle implementations and invokes NewWithMode (e.g. adapting a bundle meant for xDS fallback or old xdstpb-style mode-based credentials). Also seen when porting examples that assumed a multi-mode bundle.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- overriding server name is not supported by xDS client TLS…
- failed to build credentials bundle from bootstrap for
- grpctransport: config
- grpctransport: unknown config name
- security configuration on the client-side does not contain…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/96a6424b1d09afb4.
Report an issue: GitHub.
Appendix: 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 0c51461d27)