{"record":{"id":"242b38fee12e2808","repo":"grpc/grpc-go","slug":"upstreamtlscontext-in-cds-response-does-not-contai","errorCode":null,"errorMessage":"UpstreamTlsContext in CDS response does not contain a CommonTlsContext","messagePattern":"UpstreamTlsContext in CDS response does not contain a CommonTlsContext","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/xdsclient/xdsresource/unmarshal_cds.go","lineNumber":367,"sourceCode":"\t\ttc = ts.GetTypedConfig()\n\t\ttypeURL = tc.GetTypeUrl()\n\t}\n\n\tif name := ts.GetName(); name != transportSocketName {\n\t\treturn nil, false, fmt.Errorf(\"transport_socket field has unexpected name: %s\", name)\n\t}\n\tif typeURL != version.V3UpstreamTLSContextURL {\n\t\treturn nil, false, fmt.Errorf(\"transport_socket missing typed_config or wrong type_url: %q\", typeURL)\n\t}\n\tupstreamCtx := &v3tlspb.UpstreamTlsContext{}\n\tif err := proto.Unmarshal(tc.GetValue(), upstreamCtx); err != nil {\n\t\treturn nil, false, fmt.Errorf(\"failed to unmarshal UpstreamTlsContext in CDS response: %v\", err)\n\t}\n\t// The following fields from `UpstreamTlsContext` are ignored:\n\t// - allow_renegotiation\n\t// - max_session_keys\n\tif upstreamCtx.GetCommonTlsContext() == nil {\n\t\treturn nil, false, errors.New(\"UpstreamTlsContext in CDS response does not contain a CommonTlsContext\")\n\t}\n\n\tsc, err := securityConfigFromCommonTLSContext(upstreamCtx.GetCommonTlsContext(), false)\n\tif err != nil {\n\t\treturn nil, false, err\n\t}\n\t// Set SNI related fields in SecurityConfig from UpstreamTlsContext if\n\t// `GRPC_EXPERIMENTAL_XDS_SNI` is enabled.\n\tif envconfig.XDSSNIEnabled {\n\t\tsc.SNI = upstreamCtx.GetSni()\n\t\tif len(sc.SNI) > maxSNILength {\n\t\t\treturn nil, false, fmt.Errorf(\"SNI value %q in UpstreamTlsContext in CDS response exceeds max length of %d\", sc.SNI, maxSNILength)\n\t\t}\n\t\tsc.UseAutoHostSNI = upstreamCtx.GetAutoHostSni()\n\t\tsc.AutoSNISANValidation = upstreamCtx.GetAutoSniSanValidation()\n\t}\n\treturn sc, isHTTP11ProxyEnabled, nil\n}","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/xdsclient/xdsresource/unmarshal_cds.go#L349-L385","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (control-plane cluster config, simplified)\ntransport_socket:\n  name: tls_context\n  typed_config:\n    \"@type\": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext\n    # common_tls_context omitted -> error\n\n// after\ntransport_socket:\n  name: tls_context\n  typed_config:\n    \"@type\": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext\n    common_tls_context:\n      tls_certificates:\n        - certificate_chain: { filename: \"/etc/mtls/client.pem\" }\n          private_key:    { filename: \"/etc/mtls/client.key\" }\n      validation_context:\n        trusted_ca: { filename: \"/etc/mtls/ca.pem\" }","handlingStrategy":"validation","validationCode":"// Validate an UpstreamTlsContext proto the server returned, before trusting it.\nfunc validateUpstreamTlsContext(utc *tlspb.UpstreamTlsContext) error {\n    if utc == nil {\n        return errors.New(\"UpstreamTlsContext is nil\")\n    }\n    if utc.GetCommonTlsContext() == nil {\n        return errors.New(\"UpstreamTlsContext is missing CommonTlsContext\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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)."],"tags":["grpc","xds","cds","tls","upstream-tls-context","validation","control-plane"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}