grpc/grpc-java · error · IllegalArgumentException

Invalid header matcher config: [grpc-] prefixed header name…

Error message

Invalid header matcher config: [grpc-] prefixed header name is not allowed.

What it means

This error is thrown by RbacFilter.parseHeaderMatcher when an xDS RBAC policy's header matcher specifies a header name starting with the reserved 'grpc-' prefix. The gRPC-internal 'grpc-*' metadata keys are reserved for the transport and must not be matched by RBAC rules, so the config is rejected at parse time with IllegalArgumentException.

Solutions

  1. Remove or rename the header matcher so it does not use a name starting with 'grpc-'
  2. Match on a non-reserved application header instead (e.g. custom 'x-' headers)
  3. Validate header names before submitting the xDS config to the management server

Example fix

// before
{"header": {"name": "grpc-timeout", "exactMatch": "10s"}}
// after
{"header": {"name": "x-request-tier", "exactMatch": "premium"}}
Defensive patterns

Strategy: validation

Validate before calling

if (headerMatcher.getName().startsWith("grpc-")) {
  throw new IllegalArgumentException("Reserved grpc- header name not allowed in RBAC matcher: " + headerMatcher.getName());
}

Prevention

When it happens

Trigger: Parsing an xDS RBAC config (via parsePermission or parsePrincipal) whose v3 HeaderMatcher proto has name like 'grpc-timeout' or 'grpc-encoding'.

Common situations: Hand-written RBAC policy JSON/YAML copied from HTTP proxy configs that reference grpc-* headers; tools generating RBAC policies that include internal gRPC transport headers.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at xds/src/main/java/io/grpc/xds/RbacFilter.java:325

    switch (proto.getRuleCase()) {
      case PATH:
        return PathMatcher.create(MatcherParser.parseStringMatcher(proto.getPath()));
      case RULE_NOT_SET:
      default:
        throw new IllegalArgumentException(
                "Unknown path matcher rule type: " + proto.getRuleCase());
    }
  }

  private static RequestedServerNameMatcher parseRequestedServerNameMatcher(
          io.envoyproxy.envoy.type.matcher.v3.StringMatcher proto) {
    return RequestedServerNameMatcher.create(MatcherParser.parseStringMatcher(proto));
  }

  private static AuthHeaderMatcher parseHeaderMatcher(
          io.envoyproxy.envoy.config.route.v3.HeaderMatcher proto) {
    if (proto.getName().startsWith("grpc-")) {
      throw new IllegalArgumentException("Invalid header matcher config: [grpc-] prefixed "
          + "header name is not allowed.");
    }
    if (":scheme".equals(proto.getName())) {
      throw new IllegalArgumentException("Invalid header matcher config: header name [:scheme] "
          + "is not allowed.");
    }
    return AuthHeaderMatcher.create(MatcherParser.parseHeaderMatcher(proto));
  }

  private static AuthenticatedMatcher parseAuthenticatedMatcher(
          Principal.Authenticated proto) {
    Matchers.StringMatcher matcher = MatcherParser.parseStringMatcher(proto.getPrincipalName());
    return AuthenticatedMatcher.create(matcher);
  }

  private static DestinationPortMatcher createDestinationPortMatcher(int port) {
    return DestinationPortMatcher.create(port);
  }

View on GitHub (pinned to 64daddc1f3)