grpc/grpc-go · error

unsupported mode

Error message

unsupported mode: %v

What it means

Returned by (*creds).NewWithMode when the requested mode string is not one of the three internal constants (CredsBundleModeFallback, CredsBundleModeBackendFromBalancer, CredsBundleModeBalancer). This is an internal gRPC API: external callers should not pass arbitrary mode strings. The %v echoes the offending mode.

Solutions

  1. Stop calling NewWithMode with custom strings; use only the public constructors (NewDefaultCredentials, NewComputeEngineCredentials).
  2. If you must call NewWithMode, import the mode constants from google.golang.org/grpc/internal (or prefer the public bundle API) and never pass literals.
  3. Align gRPC versions across modules so the internal constant values are consistent.

Example fix

// before
bundle, err := c.NewWithMode("custom-mode")
// after
bundle, err := c.NewWithMode(internal.CredsBundleModeFallback)
Defensive patterns

Strategy: validation

Validate before calling

// Do not construct custom mode strings; use only the public bundle constructors.
// If you must call NewWithMode, validate against the known set:
func isValidMode(m string) bool {
    return m == internal.CredsBundleModeFallback ||
        m == internal.CredsBundleModeBackendFromBalancer ||
        m == internal.CredsBundleModeBalancer
}
if !isValidMode(mode) {
    return fmt.Errorf("unsupported mode %q; use a public credentials constructor", mode)
}

Try / catch

bundle, err := c.NewWithMode(mode)
if err != nil && strings.HasPrefix(err.Error(), "unsupported mode") {
    log.Fatalf("internal API misuse: %v", err)
}

Prevention

When it happens

Trigger: Calling the Bundle.NewWithMode method on a google credentials bundle with a custom or empty mode string; a version mismatch where internal mode constant values changed but a caller hardcodes a literal.

Common situations: Custom resolvers or balancer plugins that invoke NewWithMode with a mode not defined in internal/internal.go; copy-pasted code from an older gRPC version using now-renamed constants; testing harnesses that fuzz the mode parameter.

Related errors


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

Appendix: source

Thrown at credentials/google/google.go:146

// NewWithMode should make a copy of Bundle, and switch mode. Modifying the
// existing Bundle may cause races.
func (c *creds) NewWithMode(mode string) (credentials.Bundle, error) {
	newCreds := &creds{
		opts: c.opts,
		mode: mode,
	}

	// Create transport credentials.
	switch mode {
	case internal.CredsBundleModeFallback:
		newCreds.transportCreds = newClusterTransportCreds(newTLS(), newALTS())
	case internal.CredsBundleModeBackendFromBalancer, internal.CredsBundleModeBalancer:
		// Only the clients can use google default credentials, so we only need
		// to create new ALTS client creds here.
		newCreds.transportCreds = newALTS()
	default:
		return nil, fmt.Errorf("unsupported mode: %v", mode)
	}

	if mode == internal.CredsBundleModeFallback || mode == internal.CredsBundleModeBackendFromBalancer {
		newCreds.perRPCCreds = newCreds.opts.PerRPCCreds
	}

	return newCreds, nil
}

// dualPerRPCCreds implements credentials.PerRPCCredentials by embedding the
// fallback PerRPCCredentials and the ALTS one. It pickes one of them based on
// the channel type.
type dualPerRPCCreds struct {
	perRPCCreds     credentials.PerRPCCredentials
	altsPerRPCCreds credentials.PerRPCCredentials
}

func (d *dualPerRPCCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {

View on GitHub (pinned to 0c51461d27)