grpc/grpc-java · error · ResourceInvalidException

HttpConnectionManager with xff_num_trusted_hops unsupported

Error message

HttpConnectionManager with xff_num_trusted_hops unsupported

What it means

gRPC xDS does not implement xff_num_trusted_hops on the HttpConnectionManager, which controls how many trusted proxy hops are counted when interpreting X-Forwarded-For. If the field is nonzero the HCM is considered unsupported and a ResourceInvalidException is thrown, rejecting the Listener.

Source

Thrown at xds/src/main/java/io/grpc/xds/XdsListenerResource.java:499

        throw new ResourceInvalidException("Unknown source-type: " + proto.getSourceType());
    }
    return FilterChainMatch.create(
        proto.getDestinationPort().getValue(),
        prefixRanges.build(),
        ImmutableList.copyOf(proto.getApplicationProtocolsList()),
        sourcePrefixRanges.build(),
        sourceType,
        ImmutableList.copyOf(proto.getSourcePortsList()),
        ImmutableList.copyOf(proto.getServerNamesList()),
        proto.getTransportProtocol());
  }

  @VisibleForTesting
  static io.grpc.xds.HttpConnectionManager parseHttpConnectionManager(
      HttpConnectionManager proto, FilterRegistry filterRegistry,
      boolean isForClient, XdsResourceType.Args args) throws ResourceInvalidException {
    if (proto.getXffNumTrustedHops() != 0) {
      throw new ResourceInvalidException(
          "HttpConnectionManager with xff_num_trusted_hops unsupported");
    }
    if (!proto.getOriginalIpDetectionExtensionsList().isEmpty()) {
      throw new ResourceInvalidException("HttpConnectionManager with "
          + "original_ip_detection_extensions unsupported");
    }
    // Obtain max_stream_duration from Http Protocol Options.
    long maxStreamDuration = 0;
    if (proto.hasCommonHttpProtocolOptions()) {
      HttpProtocolOptions options = proto.getCommonHttpProtocolOptions();
      if (options.hasMaxStreamDuration()) {
        maxStreamDuration = Durations.toNanos(options.getMaxStreamDuration());
      }
    }

    // Parse http filters.
    if (proto.getHttpFiltersList().isEmpty()) {
      throw new ResourceInvalidException("Missing HttpFilter in HttpConnectionManager.");

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Remove xff_num_trusted_hops (or set it to 0) from the http_connection_manager.
  2. Handle X-Forwarded-For interpretation at an upstream proxy/Envoy layer instead of the gRPC client.
  3. Split the config so Envoy-managed listeners keep the setting while gRPC-consumed resources do not.

Example fix

// before
http_connection_manager: { xff_num_trusted_hops: 2, ... }
// after
http_connection_manager: { ... }
Defensive patterns

Strategy: validation

Validate before calling

if (hcm.getXffNumTrustedHops() != 0) {
  throw new IllegalArgumentException("xff_num_trusted_hops unsupported by grpc xds");
}

Try / catch

try { applyResource(listener) } catch (ResourceInvalidException e) { if (e.getMessage().contains("xff_num_trusted_hops")) resubmitWithoutXffHops(); }

Prevention

When it happens

Trigger: Listener's http_connection_manager has xff_num_trusted_hops set to a nonzero value, detected in parseHttpConnectionManager (invoked from processClientSideListener or httpConnectionManager parsing).

Common situations: Envoy configs deployed behind multiple load balancers where operators set trusted hops to make XFF parsing correct, shared Envoy templates reused for gRPC xDS listeners, or control planes that propagate the field unconditionally.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/bfa7793acec4f58d. Report an issue: GitHub.