{"record":{"id":"be3340e6abd347df","repo":"apache/druid","slug":"escape-must-be-null-or-a-single-character","errorCode":null,"errorMessage":"Escape must be null or a single character","messagePattern":"Escape must be null or a single character","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/filter/LikeDimFilter.java","lineNumber":78,"sourceCode":"  private final FilterTuning filterTuning;\n  private final LikeMatcher likeMatcher;\n\n  @JsonCreator\n  public LikeDimFilter(\n      @JsonProperty(\"dimension\") final String dimension,\n      @JsonProperty(\"pattern\") final String pattern,\n      @JsonProperty(\"escape\") @Nullable final String escape,\n      @JsonProperty(\"extractionFn\") @Nullable final ExtractionFn extractionFn,\n      @JsonProperty(\"filterTuning\") @Nullable final FilterTuning filterTuning\n  )\n  {\n    this.dimension = Preconditions.checkNotNull(dimension, \"dimension\");\n    this.pattern = Preconditions.checkNotNull(pattern, \"pattern\");\n    this.extractionFn = extractionFn;\n    this.filterTuning = filterTuning;\n\n    if (escape != null && escape.length() != 1) {\n      throw new IllegalArgumentException(\"Escape must be null or a single character\");\n    } else {\n      this.escapeChar = escape == null ? null : escape.charAt(0);\n    }\n\n    this.likeMatcher = LikeMatcher.from(pattern, this.escapeChar);\n  }\n\n  @VisibleForTesting\n  public LikeDimFilter(\n      final String dimension,\n      final String pattern,\n      @Nullable final String escape,\n      @Nullable final ExtractionFn extractionFn\n  )\n  {\n    this(dimension, pattern, escape, extractionFn, null);\n  }\n","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/filter/LikeDimFilter.java#L60-L96","documentation":"LikeDimFilter validates that its escape argument is either null (no escaping) or exactly one character, since escapes operate per character in LIKE-style pattern matching. Any longer string is rejected at construction with IllegalArgumentException.","triggerScenarios":"Constructing new LikeDimFilter(...) or deserializing a JSON like filter where \"escape\" is a multi-character string, e.g. \"escape\": \"\\\\\\\\\".","commonSituations":"Users writing JSON filters who mistake escape for an escape string/prefix (like SQL ESCAPE clauses with multiple chars) or who paste two characters by accident.","solutions":["Change the escape value to a single character, or remove it (null) entirely.","If a multi-character escape sequence is needed, pre-process the pattern manually with LikeMatcher semantics instead of using escape.","Validate user-supplied escape input in your API layer before building the filter."],"exampleFix":"// before\nnew LikeDimFilter(\"dim\", \"foo\\\\%bar\", \"\\\\%\", null);\n// after\nnew LikeDimFilter(\"dim\", \"foo\\\\%bar\", \"\\\\\", null); // single-char escape","handlingStrategy":"validation","validationCode":"if (escape != null && escape.length() != 1) { throw new IllegalArgumentException(\"escape must be null or a single character\"); }","typeGuard":"boolean validEscape = escape == null || escape.length() == 1;","tryCatchPattern":"try { new LikeDimFilter(dim, pattern, escape, tuning); } catch (IllegalArgumentException e) { /* sanitize escape and retry */ }","preventionTips":["Validate user-supplied escape strings in your API layer","Use null escape unless per-character escaping is truly needed","Remember escape is one char, not an escape prefix string"],"tags":["druid","filter","validation","illegal-argument"],"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-14T11:17:12.474Z"}