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
- Inspect the cluster resource ( LDS/CDS dump from the server) to confirm whether common_tls_context is set inside upstream_tls_context.
- 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).
- If a plaintext upstream is intended, do not include an UpstreamTlsContext typed_config at all.
- 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
- On the control plane, always populate common_tls_context inside upstream_tls_context for gRPC-bound clusters.
- If a plaintext upstream is intended, omit the transport_socket/typed_config entirely rather than sending an empty one.
- Run a config-drift check between your cluster definitions and the gRPC xDS expectations (gRFC A47/A27).
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
- security configuration on the client-side does not contain…
- security configuration on the server-side does not contain…
- DownstreamTlsContext in LDS response does not contain a…
- empty contains is not allowed in StringMatcher
- empty prefix is not allowed in StringMatcher
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)