apache/druid · error · IllegalArgumentException
Function[ ] substring must be a string literal
Error message
Function[%s] substring must be a string literal
What it means
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.
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
Example fix
// before contains(channel, otherColumn) // after contains(channel, 'news')
Defensive patterns
Strategy: validation
Validate before calling
if (!(searchArg instanceof String)) {
throw new IllegalArgumentException("contains() substring must be a string literal");
} Type guard
boolean isLiteralString = expr != null && expr.isLiteral() && expr.getLiteralValue() instanceof String;
Try / catch
try {
Expr parsed = Parser.parse(exprString);
} catch (IAE e) {
if (e.getMessage().contains("must be a string literal")) { /* use like filter instead */ }
} Prevention
- Pass quoted literals to contains()/icontains()
- Use the like filter or BoundFilter for dynamic patterns
- Escape quotes when generating expressions programmatically
When it happens
Trigger: Calling contains(...)/icontains(...) with a column or computed expression as the second argument instead of a quoted string literal.
Common situations: Trying to search with a value from another column; building SQL LIKE-like dynamic queries through the Druid expression API.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- rank must be a number
- Time zone must be a literal
- argument must be a LONG constant
- attempt to get boolean[] null vector from string[] only…
- attempt to get double[] from string[] only scalar binding
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/264a8449a048f684.
Report an issue: GitHub.
Appendix: source
Thrown at processing/src/main/java/org/apache/druid/query/expression/ContainsExpr.java:89
if (s == null) {
// same behavior as regexp_like.
return ExprEval.ofLong(null);
} else {
final boolean doesContain = searchFunction.apply(s);
return ExprEval.ofLongBoolean(doesContain);
}
}
@Override
public ExpressionType getOutputType(InputBindingInspector inspector)
{
return ExpressionType.LONG;
}
private static String getSearchString(Expr searchExpr, String functioName)
{
if (!ExprUtils.isStringLiteral(searchExpr)) {
throw new IAE("Function[%s] substring must be a string literal", functioName);
}
return StringUtils.nullToEmptyNonDruidDataString((String) searchExpr.getLiteralValue());
}
private static Function<String, Boolean> createFunction(String searchString, boolean caseSensitive)
{
if (caseSensitive) {
return s -> s.contains(searchString);
} else {
return s -> org.apache.commons.lang3.StringUtils.containsIgnoreCase(s, searchString);
}
}
}
View on GitHub (pinned to 9b90983fd2)