{"record":{"id":"b8229630f27f9d79","repo":"grpc/grpc-java","slug":"andmatcher-must-have-at-least-2-predicates","errorCode":null,"errorMessage":"AndMatcher must have at least 2 predicates","messagePattern":"AndMatcher must have at least 2 predicates","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/PredicateEvaluator.java","lineNumber":143,"sourceCode":"    }\n    \n    @Override \n    boolean evaluate(MatchContext context) {\n      for (PredicateEvaluator e : evaluators) {\n        if (e.evaluate(context)) {\n          return true;\n        }\n      }\n      return false;\n    }\n  }\n\n  private static final class AndMatcherEvaluator extends PredicateEvaluator {\n    private final List<PredicateEvaluator> evaluators;\n    \n    AndMatcherEvaluator(Predicate.PredicateList proto) {\n      if (proto.getPredicateCount() < 2) {\n        throw new IllegalArgumentException(\"AndMatcher must have at least 2 predicates\");\n      }\n      this.evaluators = new ArrayList<>(proto.getPredicateCount());\n      for (Predicate p : proto.getPredicateList()) {\n        evaluators.add(PredicateEvaluator.fromProto(p));\n      }\n    }\n    \n    @Override \n    boolean evaluate(MatchContext context) {\n      for (PredicateEvaluator e : evaluators) {\n        if (!e.evaluate(context)) {\n          return false;\n        }\n      }\n      return true;\n    }\n  }\n","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/PredicateEvaluator.java#L125-L161","documentation":"An AndMatcherEvaluator represents a PredicateList under and_matcher; an AND over fewer than 2 predicates is a no-op, so the constructor throws IllegalArgumentException when the proto contains fewer than 2 predicates.","triggerScenarios":"Building AndMatcherEvaluator from a Predicate.PredicateList proto with getPredicateCount() < 2 — e.g. and_matcher set to an empty list after predicates were removed, or a lone predicate wrapped in and_matcher unnecessarily.","commonSituations":"Config templating that emits empty and_matcher placeholders; management servers pruning predicates without collapsing the and_matcher; hand-written matcher trees with vestigial and_matcher wrappers.","solutions":["Ensure and_matcher contains at least 2 predicates in the config","Replace a single-predicate and_matcher with the predicate directly","Pre-validate that predicate lists under and_matcher have count >= 2 before parsing"],"exampleFix":"// before\nand_matcher:\n  predicate:\n  - single_predicate: { ... }\n// after\nand_matcher:\n  predicate:\n  - single_predicate: { ... }\n  - single_predicate: { ... }","handlingStrategy":"validation","validationCode":"if (proto.getPredicateCount() < 2) {\n  throw new IllegalArgumentException(\"and_matcher requires >= 2 predicates\");\n}","typeGuard":null,"tryCatchPattern":"try { new PredicateEvaluator.AndMatcherEvaluator(proto); } catch (IllegalArgumentException e) { log.warn(\"and_matcher with <2 predicates: \" + e.getMessage()); }","preventionTips":["Collapse single-clause and_matchers into the predicate itself","Validate predicate list lengths on the control plane","Add config linting for minimum list sizes"],"tags":["xds","matcher","config-validation","grpc"],"backgroundTag":"value-out-of-range","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"}