kestra-io/kestra · error · PebbleException

RegexTimeoutException: e.getMessage()

Error message

RegexTimeoutException: e.getMessage()

What it means

Thrown by the 'regexReplace' Pebble filter when the underlying `RegexUtils.matcher(...).replaceAll(replacement)` call raises a `RegexUtils.RegexTimeoutException`. Kestra wraps regex execution in a timeout guard to prevent catastrophic backtracking (ReDoS) from hanging task evaluation; exceeding it surfaces as this error.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/filters/RegexReplaceFilter.java:75

        }

        if (args.get(ARGUMENT_REPLACEMENT) == null) {
            throw new PebbleException(
                null,
                MessageFormat.format("The argument ''{0}'' is required.", ARGUMENT_REPLACEMENT),
                lineNumber,
                self.getName()
            );
        }

        String regex = args.get(ARGUMENT_REGEX).toString();
        String replacement = args.get(ARGUMENT_REPLACEMENT).toString();
        try {
            return RegexUtils.matcher(Pattern.compile(regex), input.toString()).replaceAll(replacement);
        } catch (PatternSyntaxException e) {
            throw new PebbleException(e, MessageFormat.format("Invalid regex ''{0}'': {1}", regex, e.getDescription()), lineNumber, self.getName());
        } catch (RegexUtils.RegexTimeoutException e) {
            throw new PebbleException(e, e.getMessage(), lineNumber, self.getName());
        }
    }
}

View on GitHub (pinned to 823fada927)

Solutions

  1. Simplify the regex to a linear-time equivalent (avoid nested unbounded quantifiers).
  2. Anchor the pattern and use possessive quantifiers or atomic groups where supported.
  3. Pre-truncate or pre-filter very long input before applying the regex.
  4. If the match is the goal, switch to `regexMatch` or `regexExtract` which may short-circuit faster than `replaceAll`.

Example fix

# before - catastrophic backtracking
{{ log | regexReplace(regex="(a+)+b", replacement="x") }}
# after - linear
{{ log | regexReplace(regex="a+b", replacement="x") }}
Defensive patterns

Strategy: validation

Validate before calling

# Rewrite vulnerable patterns to linear equivalents and pre-truncate input:
{% set safeInput = (log ?? "") | slice(0, 10000) %}
{{ safeInput | regexReplace(regex="a+b", replacement="x") }}

Prevention

When it happens

Trigger: A regex prone to catastrophic backtracking applied to a long or adversarial input string (e.g. `(a+)+$` against a string of 'a's); a pattern with nested quantifiers and no anchoring.

Common situations: User-supplied input flowing into a regex without sanitization; copy-pasted regex from Stack Overflow that is not linear; large log lines being processed.

Understand the failure class

Related errors


AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14). Data as JSON: /api/errors/cfa85843770c29df. Report an issue: GitHub.