grpc/grpc-go · error

UpstreamTlsContext in CDS response does not contain a…

Error message

UpstreamTlsContext in CDS response does not contain a CommonTlsContext

What it means

While unmarshaling a CDS (Cluster Discovery Service) response, internal/xds/xdsclient/xdsresource/unmarshal_cds.go extracts the UpstreamTlsContext from the cluster's transport_socket and then requires it to carry a CommonTlsContext. After unmarshaling at line 359-362, the guard at line 366-367 returns this error if `upstreamCtx.GetCommonTlsContext() == nil`. The CommonTlsContext is the part of the proto that actually carries TLS certs, validation context, and ALPN — without it the upstream TLS configuration is unusable.

Solutions

  1. Inspect the cluster resource ( LDS/CDS dump from the server) to confirm whether common_tls_context is set inside upstream_tls_context.
  2. Fix the server-side cluster configuration to provide a valid CommonTlsContext (e.g. with tls_certificate_certificate_chain, validation_context, or a bundled TLS provider).
  3. If a plaintext upstream is intended, do not include an UpstreamTlsContext typed_config at all.
  4. Upgrade the xDS server to a version known to emit well-formed UpstreamTlsContexts for gRPC clients.

Example fix

// before (control-plane cluster config, simplified)
transport_socket:
  name: tls_context
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
    # common_tls_context omitted -> error

// after
transport_socket:
  name: tls_context
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
    common_tls_context:
      tls_certificates:
        - certificate_chain: { filename: "/etc/mtls/client.pem" }
          private_key:    { filename: "/etc/mtls/client.key" }
      validation_context:
        trusted_ca: { filename: "/etc/mtls/ca.pem" }
Defensive patterns

Strategy: validation

Validate before calling

// Validate an UpstreamTlsContext proto the server returned, before trusting it.
func validateUpstreamTlsContext(utc *tlspb.UpstreamTlsContext) error {
    if utc == nil {
        return errors.New("UpstreamTlsContext is nil")
    }
    if utc.GetCommonTlsContext() == nil {
        return errors.New("UpstreamTlsContext is missing CommonTlsContext")
    }
    return nil
}

Prevention

When it happens

Trigger: Triggered when the xDS server returns a cluster whose UpstreamTlsContext is present (the typed_config type URL matched UpstreamTLSContextURL) but its `common_tls_context` field is unset. The error is returned from securityConfigFromCluster to the CDS unmarshaler, causing the cluster resource to be NACK'd.

Common situations: A control plane misconfiguration that supplies a transport_socket typed_config but leaves common_tls_context empty; a partial upgrade where the server emits the outer UpstreamTlsContext but not the inner CommonTlsContext; an Envoy/Istio version emitting a non-standard TLS context; an xDS server bug.

Related errors


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

Appendix: source

Thrown at internal/xds/xdsclient/xdsresource/unmarshal_cds.go:367

		tc = ts.GetTypedConfig()
		typeURL = tc.GetTypeUrl()
	}

	if name := ts.GetName(); name != transportSocketName {
		return nil, false, fmt.Errorf("transport_socket field has unexpected name: %s", name)
	}
	if typeURL != version.V3UpstreamTLSContextURL {
		return nil, false, fmt.Errorf("transport_socket missing typed_config or wrong type_url: %q", typeURL)
	}
	upstreamCtx := &v3tlspb.UpstreamTlsContext{}
	if err := proto.Unmarshal(tc.GetValue(), upstreamCtx); err != nil {
		return nil, false, fmt.Errorf("failed to unmarshal UpstreamTlsContext in CDS response: %v", err)
	}
	// The following fields from `UpstreamTlsContext` are ignored:
	// - allow_renegotiation
	// - max_session_keys
	if upstreamCtx.GetCommonTlsContext() == nil {
		return nil, false, errors.New("UpstreamTlsContext in CDS response does not contain a CommonTlsContext")
	}

	sc, err := securityConfigFromCommonTLSContext(upstreamCtx.GetCommonTlsContext(), false)
	if err != nil {
		return nil, false, err
	}
	// Set SNI related fields in SecurityConfig from UpstreamTlsContext if
	// `GRPC_EXPERIMENTAL_XDS_SNI` is enabled.
	if envconfig.XDSSNIEnabled {
		sc.SNI = upstreamCtx.GetSni()
		if len(sc.SNI) > maxSNILength {
			return nil, false, fmt.Errorf("SNI value %q in UpstreamTlsContext in CDS response exceeds max length of %d", sc.SNI, maxSNILength)
		}
		sc.UseAutoHostSNI = upstreamCtx.GetAutoHostSni()
		sc.AutoSNISANValidation = upstreamCtx.GetAutoSniSanValidation()
	}
	return sc, isHTTP11ProxyEnabled, nil
}

View on GitHub (pinned to 0c51461d27)