apache/druid · error · DruidException

replacement must be a string literal

Error message

replacement must be a string literal

What it means

regexp_replace requires the replacement argument (third argument) to be a string literal, or an explicit literal NULL. Non-literal replacements cannot be embedded in the precompiled replace expression, so validation fails.

Source

Thrown at processing/src/main/java/org/apache/druid/query/expression/RegexpReplaceExprMacro.java:93

    private final Expr arg;
    private final Pattern pattern;
    private final String replacement;

    private RegexpReplaceExpr(List<Expr> args)
    {
      super(args);

      final Expr patternExpr = args.get(1);
      final Expr replacementExpr = args.get(2);

      if (!ExprUtils.isStringLiteral(patternExpr)
          && !(patternExpr.isLiteral() && patternExpr.getLiteralValue() == null)) {
        throw validationFailed("pattern must be a string literal");
      }

      if (!ExprUtils.isStringLiteral(replacementExpr)
          && !(replacementExpr.isLiteral() && replacementExpr.getLiteralValue() == null)) {
        throw validationFailed("replacement must be a string literal");
      }

      final String patternString = (String) patternExpr.getLiteralValue();

      this.arg = args.get(0);
      this.pattern = patternString != null ? RegexpExprUtils.compilePattern(patternString, FN_NAME) : null;
      this.replacement = (String) replacementExpr.getLiteralValue();
    }

    @Nonnull
    @Override
    public ExprEval<?> eval(final ObjectBinding bindings)
    {
      if (pattern == null || replacement == null) {
        return ExprEval.ofString(null);
      }

      final String s = arg.eval(bindings).asString();

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Inline the replacement as a quoted string literal
  2. Use literal NULL only if you intend null replacement semantics
  3. Perform substitution with a different expression (e.g. CONCAT) if the replacement must be dynamic

Example fix

// before
REGEXP_REPLACE(col, '[aeiou]', repl_col)
// after
REGEXP_REPLACE(col, '[aeiou]', '-')
Defensive patterns

Strategy: validation

Validate before calling

boolean ok = ExprUtils.isStringLiteral(replacementExpr)
    || (replacementExpr.isLiteral() && replacementExpr.getLiteralValue() == null);
if (!ok) {
  throw new IllegalArgumentException("replacement must be a string literal");
}

Type guard

static boolean isStringOrNullLiteral(Expr e) {
  return ExprUtils.isStringLiteral(e) || (e.isLiteral() && e.getLiteralValue() == null);
}

Try / catch

try {
  return macro.apply(args);
} catch (ExpressionValidationException e) {
  log.error("regexp_replace replacement invalid: %s", e.getMessage());
  throw e;
}

Prevention

When it happens

Trigger: REGEXP_REPLACE(expr, 'pattern', repl) where repl is a column, expression, or non-string non-null literal.

Common situations: Replacement strings pulled from other columns; passing numbers or booleans as the replacement; templated query generation injecting non-literal values.

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


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/0ac8fe372b4b5275. Report an issue: GitHub.