{"record":{"id":"264a8449a048f684","repo":"apache/druid","slug":"function-s-substring-must-be-a-string-literal","errorCode":null,"errorMessage":"Function[%s] substring must be a string literal","messagePattern":"Function\\[(.+?)\\] substring must be a string literal","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/expression/ContainsExpr.java","lineNumber":89,"sourceCode":"    if (s == null) {\n      // same behavior as regexp_like.\n      return ExprEval.ofLong(null);\n    } else {\n      final boolean doesContain = searchFunction.apply(s);\n      return ExprEval.ofLongBoolean(doesContain);\n    }\n  }\n\n  @Override\n  public ExpressionType getOutputType(InputBindingInspector inspector)\n  {\n    return ExpressionType.LONG;\n  }\n\n  private static String getSearchString(Expr searchExpr, String functioName)\n  {\n    if (!ExprUtils.isStringLiteral(searchExpr)) {\n      throw new IAE(\"Function[%s] substring must be a string literal\", functioName);\n    }\n    return StringUtils.nullToEmptyNonDruidDataString((String) searchExpr.getLiteralValue());\n  }\n\n  private static Function<String, Boolean> createFunction(String searchString, boolean caseSensitive)\n  {\n    if (caseSensitive) {\n      return s -> s.contains(searchString);\n    } else {\n      return s -> org.apache.commons.lang3.StringUtils.containsIgnoreCase(s, searchString);\n    }\n  }\n}\n","sourceCodeStart":71,"sourceCodeEnd":103,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/expression/ContainsExpr.java#L71-L103","documentation":"ContainsExpr (contains/icontains string functions) requires the substring argument to be a string literal so it can compile to a constant predicate. If the search expression is not a literal string (e.g. a column reference, concatenated expression, or null), the function cannot be evaluated statically and Druid throws this IAE.","triggerScenarios":"Calling contains(...)/icontains(...) with a column or computed expression as the second argument instead of a quoted string literal.","commonSituations":"Trying to search with a value from another column; building SQL LIKE-like dynamic queries through the Druid expression API.","solutions":["Pass a quoted string literal: contains(col, 'substr')","Use SQL with LIKE or a filter (e.g. BoundFilter/like filter) for dynamic patterns","Compute the constant in application code before issuing the query"],"exampleFix":"// before\ncontains(channel, otherColumn)\n// after\ncontains(channel, 'news')","handlingStrategy":"validation","validationCode":"if (!(searchArg instanceof String)) {\n  throw new IllegalArgumentException(\"contains() substring must be a string literal\");\n}","typeGuard":"boolean isLiteralString = expr != null && expr.isLiteral() && expr.getLiteralValue() instanceof String;","tryCatchPattern":"try {\n  Expr parsed = Parser.parse(exprString);\n} catch (IAE e) {\n  if (e.getMessage().contains(\"must be a string literal\")) { /* use like filter instead */ }\n}","preventionTips":["Pass quoted literals to contains()/icontains()","Use the like filter or BoundFilter for dynamic patterns","Escape quotes when generating expressions programmatically"],"tags":["expression","query","argument-type"],"backgroundTag":"invalid-argument-value","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}