{"record":{"id":"30ca2741b9a9d0b8","repo":"grpc/grpc-java","slug":"celmatcher-must-have-expr-match","errorCode":null,"errorMessage":"CelMatcher must have expr_match","messagePattern":"CelMatcher must have expr_match","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/CelStateMatcher.java","lineNumber":57,"sourceCode":"      return compiledEndpoint.match(value);\n    } catch (CelEvaluationException e) {\n      return false;\n    }\n  }\n\n  @Override\n  public Class<?> inputType() {\n    return GrpcCelEnvironment.class;\n  }\n\n  static final class Provider implements MatcherProvider {\n    @Override\n    public CelStateMatcher getMatcher(TypedExtensionConfig config) {\n      try {\n        com.github.xds.type.matcher.v3.CelMatcher celProto = config.getTypedConfig()\n            .unpack(com.github.xds.type.matcher.v3.CelMatcher.class);\n        if (!celProto.hasExprMatch()) {\n          throw new IllegalArgumentException(\"CelMatcher must have expr_match\");\n        }\n        CelExpression expr = celProto.getExprMatch();\n        if (!expr.hasCelExprChecked()) {\n          throw new IllegalArgumentException(\"CelMatcher must have cel_expr_checked\");\n        }\n        CelAbstractSyntaxTree ast = \n            CelProtoAbstractSyntaxTree.fromCheckedExpr(\n                expr.getCelExprChecked()).getAst();\n        CelMatcher compiled = CelMatcher.compile(ast);\n        \n        return new CelStateMatcher(compiled);\n      } catch (Exception e) {\n        throw new IllegalArgumentException(\"Invalid CelMatcher config\", e);\n      }\n    }\n\n    @Override\n    public String typeUrl() {","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/CelStateMatcher.java#L39-L75","documentation":"CelStateMatcher.getMatcher() unpacks the TypedExtensionConfig's typed_config as an xDS CelMatcher proto and requires the expr_match field to be set. A CelMatcher proto without expr_match carries no CEL expression at all, so no matcher can be built. The factory rejects such configs with IllegalArgumentException.","triggerScenarios":"An xDS TypedExtensionConfig whose typed_config is type.googleapis.com/xds.type.matcher.v3.CelMatcher but with expr_match unset (oneof empty) reaches getMatcher().","commonSituations":"Control plane emitting CelMatcher protos with only defaults populated; hand-written test fixtures forgetting to call setExprMatch(); proto field name changes across xDS type versions.","solutions":["Populate expr_match in the CelMatcher proto before sending the config (proto.hasExprMatch() must be true).","Validate the extension config on the control plane side prior to xDS delivery.","Catch IllegalArgumentException from getMatcher() and reject the whole config with an NACK / clearer error."],"exampleFix":"// before\nCelMatcher.newBuilder().build(); // expr_match missing\n// after\nCelMatcher.newBuilder()\n    .setExprMatch(CelExpression.newBuilder().setCelExprChecked(checkedExpr))\n    .build();","handlingStrategy":"validation","validationCode":"CelMatcherProto.CelMatcher celProto =\n    config.getTypedConfig().unpack(CelMatcherProto.CelMatcher.class);\nif (!celProto.hasExprMatch()) throw new IllegalArgumentException(\"expr_match required\");","typeGuard":"boolean hasExprMatch(TypedExtensionConfig config) {\n  return config.getTypedConfig().is(CelMatcherProto.CelMatcher.class)\n      && unpackSafe(config).hasExprMatch();\n}","tryCatchPattern":"try { return factory.getMatcher(config); }\ncatch (IllegalArgumentException e) { nackConfig(config, \"expr_match missing: \" + e.getMessage()); return null; }","preventionTips":["Validate matcher protos control-plane side before xDS push.","Use proto builder setters (setExprMatch) so field presence is explicit.","Add fixture tests that round-trip matcher configs through serialization."],"tags":["grpc-xds","config-validation","protobuf","cel"],"backgroundTag":"missing-required-config-field","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}