kestra-io/kestra · error · PebbleException

Group index {0} is out of bounds: must be >= 0.

Error message

Group index {0} is out of bounds: must be >= 0.

What it means

Thrown by the 'regexExtract' Pebble filter when the `group` argument is a negative integer. Group 0 means the whole match and groups 1..n refer to capture groups, so negative values are meaningless and rejected before the pattern is compiled.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/filters/RegexExtractFilter.java:71

            return null;
        }

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

        String regex = args.get(ARGUMENT_REGEX).toString();
        int group = args.containsKey(ARGUMENT_GROUP) && args.get(ARGUMENT_GROUP) != null
            ? ((Number) args.get(ARGUMENT_GROUP)).intValue()
            : 0;

        if (group < 0) {
            throw new PebbleException(
                null,
                MessageFormat.format("Group index {0} is out of bounds: must be >= 0.", group),
                lineNumber,
                self.getName()
            );
        }

        Matcher matcher;
        try {
            matcher = RegexUtils.matcher(Pattern.compile(regex), input.toString());
        } catch (PatternSyntaxException e) {
            throw new PebbleException(e, MessageFormat.format("Invalid regex ''{0}'': {1}", regex, e.getDescription()), lineNumber, self.getName());
        }
        try {
            if (matcher.find()) {
                if (group > matcher.groupCount()) {
                    throw new PebbleException(
                        null,

View on GitHub (pinned to 823fada927)

Solutions

  1. Use group 0 for the whole match or a positive capture-group index.
  2. Guard dynamic indices: `group=max(0, myIndex)`.
  3. Re-read the filter Javadoc: group 0 = full match, 1..n = capture groups.

Example fix

# before
{{ s | regexExtract(regex="(a)(b)", group=idx - 1) }}
# after
{{ s | regexExtract(regex="(a)(b)", group=(idx > 0 ? idx : 0)) }}
Defensive patterns

Strategy: validation

Validate before calling

# Clamp the group index to a valid range before calling:
{{ s | regexExtract(regex="(a)(b)", group=(idx >= 0 ? idx : 0)) }}

Type guard

# Pebble: `is number` plus a >= 0 check guards the group argument:
{% set g = (groupInput is number and groupInput >= 0) ? groupInput : 0 %}

Prevention

When it happens

Trigger: Passing `group=-1` or any negative number explicitly, or passing a computed group value from a variable/inputs that evaluated to a negative number (e.g. `group=index - 1` with `index=0`).

Common situations: Off-by-one arithmetic when computing the group index dynamically; treating the group as zero-based for capture groups when it is actually 1-based (group 0 is the full match).

Related errors


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