{"record":{"id":"c889c8a8a39df624","repo":"grpc/grpc-java","slug":"celmatcher-must-have-cel-expr-checked","errorCode":null,"errorMessage":"CelMatcher must have cel_expr_checked","messagePattern":"CelMatcher must have cel_expr_checked","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/CelStateMatcher.java","lineNumber":61,"sourceCode":"  }\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() {\n      return TYPE_URL;\n    }\n  }\n}","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/CelStateMatcher.java#L43-L79","documentation":"After confirming expr_match is present, getMatcher() requires the CelExpression to contain cel_expr_checked — the type-checked AST form produced by cel compile's CheckedExpr. Without it there is no AST to convert via CelProtoAbstractSyntaxTree.fromCheckedExpr, so the config is rejected.","triggerScenarios":"expr_match is set but only holds unchecked/parsed CelExpr (or is empty), so expr.hasCelExprChecked() is false when getMatcher() unpacks the config.","commonSituations":"Clients sending source text or unchecked expressions instead of running the CEL checker and embedding CheckedExpr; tooling that serializes ParseOutput rather than CheckOutput; partially populated protos from hand-rolled builders.","solutions":["Type-check the expression with the CEL compiler and embed the result via setCelExprChecked(CheckedExpr).","Verify proto.hasCelExprChecked() on the producer side before emitting the config.","NACK the xDS config at the management layer with an explanation that only checked expressions are supported."],"exampleFix":"// before\nCelExpression.newBuilder().setCelExpr(parsedExpr).build();\n// after\nCelExpression.newBuilder().setCelExprChecked(checkOutput.getExprChecked()).build();","handlingStrategy":"validation","validationCode":"if (!expr.hasCelExprChecked()) {\n  throw new IllegalArgumentException(\"cel_expr_checked required; run CEL checker first\");\n}","typeGuard":"boolean hasCheckedExpr(CelExpression expr) { return expr.hasCelExprChecked(); }","tryCatchPattern":"try { return factory.getMatcher(config); }\ncatch (IllegalArgumentException e) { nackConfig(config, \"cel_expr_checked missing\"); return null; }","preventionTips":["Always run the CEL checker (compile with type checking) and embed CheckedExpr, never raw parsed exprs.","Verify hasCelExprChecked() before serializing configs.","Keep producer and consumer CEL toolchain versions aligned."],"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"}