grpc/grpc-go · error

unsupported field 'use_original_dst' is present and set to…

Error message

unsupported field 'use_original_dst' is present and set to true

What it means

Returned by processServerSideListener when the LDS Listener has use_original_dst set to true. grpc-go's inbound listener handling does not implement Envoy's original-destination-based filter chain selection, so this field being true is treated as an unsupported configuration and the resource is rejected (NACK).

Solutions

  1. Remove or set use_original_dst=false on the LDS Listener resource consumed by grpc-go.
  2. If original-dst routing is genuinely required, terminate LDS with Envoy/proxy rather than the in-process grpc-go xDS server.
  3. Filter the listener at the control plane so grpc-go only receives listeners without use_original_dst.

Example fix

// before
//   listener: { name: "inbound", use_original_dst: { value: true }, address: { ... } }
// after
//   listener: { name: "inbound", address: { ... } }
Defensive patterns

Strategy: validation

Validate before calling

// Reject listeners with use_original_dst before publishing to grpc-go.
func isGrpcCompatibleListener(lis *envoy_listener_pb.Listener) error {
    if lis.GetUseOriginalDst().GetValue() {
        return errors.New("use_original_dst unsupported by grpc-go xDS server; remove this field")
    }
    return nil
}

Try / catch

Catch in the LDS watcher; log and alert. The fix is in the control plane (unset the field) or in deployment topology (use Envoy as the listener).

Prevention

When it happens

Trigger: An LDS Listener destined for a gRPC server has use_original_dst=true. Typically comes from an Envoy-style config (e.g. transparent proxying / iptables redirect setups) being reused for a gRPC xDS server.

Common situations: Operator copied an Envoy Listener config (designed for transparent proxying with SO_ORIGINAL_DST) into a gRPC-managed LDS resource. Istio Pilot/ambient mesh emitted original-dst listener for a workload that grpc-go is consuming directly.

Related errors


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

Appendix: source

Thrown at internal/xds/xdsclient/xdsresource/unmarshal_lds.go:277

	}
	var i int
	for ; i < len(ret)-1; i++ {
		if ret[i].Filter.IsTerminal() {
			return nil, fmt.Errorf("http filter %q is a terminal filter but it is not last in the filter chain", ret[i].Name)
		}
	}
	if !ret[i].Filter.IsTerminal() {
		return nil, fmt.Errorf("http filter %q is not a terminal filter", ret[len(ret)-1].Name)
	}
	return ret, nil
}

func processServerSideListener(lis *v3listenerpb.Listener) (*ListenerUpdate, error) {
	if n := len(lis.ListenerFilters); n != 0 {
		return nil, fmt.Errorf("unsupported field 'listener_filters' contains %d entries", n)
	}
	if lis.GetUseOriginalDst().GetValue() {
		return nil, errors.New("unsupported field 'use_original_dst' is present and set to true")
	}
	addr := lis.GetAddress()
	if addr == nil {
		return nil, fmt.Errorf("no address field in LDS response: %+v", lis)
	}
	sockAddr := addr.GetSocketAddress()
	if sockAddr == nil {
		return nil, fmt.Errorf("no socket_address field in LDS response: %+v", lis)
	}
	lu := &ListenerUpdate{
		TCPListener: &InboundListenerConfig{
			Address: sockAddr.GetAddress(),
			Port:    strconv.Itoa(int(sockAddr.GetPortValue())),
		},
	}

	// Populate the default filter chain.
	if dfc := lis.GetDefaultFilterChain(); dfc != nil {

View on GitHub (pinned to 0c51461d27)