{"record":{"id":"9563b7f789e12eb4","repo":"grpc/grpc-java","slug":"cel-expression-must-evaluate-to-string-got-ast","errorCode":null,"errorMessage":"CEL expression must evaluate to string, got: ${ast.getResultType()}","messagePattern":"CEL expression must evaluate to string, got: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/CelStringExtractor.java","lineNumber":46,"sourceCode":" */\nfinal class CelStringExtractor {\n  private final CelRuntime.Program program;\n  @Nullable\n  private final String defaultValue;\n\n  private CelStringExtractor(CelRuntime.Program program, @Nullable String defaultValue) {\n    this.program = program;\n    this.defaultValue = defaultValue;\n  }\n\n  /**\n   * Compiles the AST into a CelStringExtractor with an optional default value.\n   * Throws an Exception if evaluation fails during compilation setup.\n   */\n  static CelStringExtractor compile(CelAbstractSyntaxTree ast, @Nullable String defaultValue)\n      throws CelEvaluationException {\n    if (ast.getResultType() != SimpleType.STRING && ast.getResultType() != SimpleType.DYN) {\n      throw new IllegalArgumentException(\n          \"CEL expression must evaluate to string, got: \" + ast.getResultType());\n    }\n    CelCommon.checkAllowedReferences(ast);\n    CelRuntime.Program program = CelCommon.RUNTIME.createProgram(ast);\n    return new CelStringExtractor(program, defaultValue);\n  }\n\n  /**\n   * Compiles the AST into a CelStringExtractor with no default value.\n   * Throws an Exception if evaluation fails during compilation setup.\n   */\n  static CelStringExtractor compile(CelAbstractSyntaxTree ast)\n      throws CelEvaluationException {\n    return compile(ast, null);\n  }\n\n  /**\n   * Evaluates the CEL expression and returns the string result.","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/CelStringExtractor.java#L28-L64","documentation":"CelStringExtractor.compile() requires the CEL AST's result type to be STRING (or DYN), because the extractor evaluates the expression to produce a header/value string for downstream matching. Any other result type is rejected at compile time with IllegalArgumentException naming the actual type.","triggerScenarios":"Calling CelStringExtractor.compile(ast, defaultValue) with an AST whose getResultType() is, e.g., SimpleType.BOOL or INT — typically a predicate expression like 'a == b' passed where a value expression like 'request.headers[\"x-user\"]' is expected.","commonSituations":"Copy-pasting a boolean matcher expression into the extractor position; control plane generating int/bool-typed attribute expressions; schema migration changing the expression's inferred type.","solutions":["Change the expression so it evaluates to a string, e.g. request.headers['x-user-id'] or string(tls.san).","Wrap non-string values with CEL conversions: string(...) for ints, or a ternary producing string branches.","If a boolean predicate is actually needed, use CelMatcher.compile() instead of CelStringExtractor.compile()."],"exampleFix":"// before\ncompile(compiler.compile(\"request.host == 'edge'\"), null); // bool\n// after\ncompile(compiler.compile(\"request.headers['x-user-id']\"), \"anonymous\");","handlingStrategy":"validation","validationCode":"if (ast.getResultType() != SimpleType.STRING && ast.getResultType() != SimpleType.DYN) {\n  throw new IllegalArgumentException(\"CEL expr must be string, got: \" + ast.getResultType());\n}","typeGuard":"boolean isStringAst(CelAbstractSyntaxTree ast) {\n  return ast.getResultType() == SimpleType.STRING || ast.getResultType() == SimpleType.DYN;\n}","tryCatchPattern":"try { return CelStringExtractor.compile(ast, def); }\ncatch (IllegalArgumentException e) { logger.warn(\"non-string CEL expr\", e); return null; }","preventionTips":["Use value expressions (request.headers['x']) not predicates for extractors.","Coerce with string(...) when the underlying value is numeric.","Provide a defaultValue to make extraction resilient."],"tags":["cel","grpc-xds","type-mismatch","extractor"],"backgroundTag":"type-mismatch","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}