kestra-io/kestra · error · PebbleException

'number' filter can only be applied to String. Actual type w

Error message

'number' filter can only be applied to String. Actual type was: {}

What it means

Thrown by the 'number' Pebble filter when the input is not a String. The filter delegates to Apache Commons `NumberUtils.createXxx` which only accepts String inputs, so any other type is rejected up front with the actual class name in the message.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/filters/NumberFilter.java:26

import io.pebbletemplates.pebble.error.PebbleException;
import io.pebbletemplates.pebble.extension.Filter;
import io.pebbletemplates.pebble.template.EvaluationContext;
import io.pebbletemplates.pebble.template.PebbleTemplate;

public class NumberFilter implements Filter {
    @Override
    public List<String> getArgumentNames() {
        return List.of("type");
    }

    @Override
    public Object apply(Object input, Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) throws PebbleException {
        if (input == null) {
            return null;
        }

        if (!(input instanceof String)) {
            throw new PebbleException(null, "'number' filter can only be applied to String. Actual type was: " + input.getClass().getName(), lineNumber, self.getName());
        }

        String type = (String) args.getOrDefault("type", "");
        String val = (String) input;

        switch (type) {
            case "INT":
                return NumberUtils.createInteger(val);
            case "FLOAT":
                return NumberUtils.createFloat(val);
            case "LONG":
                return NumberUtils.createLong(val);
            case "DOUBLE":
                return NumberUtils.createDouble(val);
            case "BIGDECIMAL":
                return NumberUtils.createBigDecimal(val);
            case "BIGINTEGER":
                return NumberUtils.createBigInteger(val);

View on GitHub (pinned to 823fada927)

Solutions

  1. Drop the `| number` filter if the value is already numeric.
  2. Stringify first if you really need to re-parse: `{{ myval | toString | number(type="INT") }}`.
  3. Check the upstream task output type declaration and align the filter chain.

Example fix

# before
{{ outputs.fetch.value | number(type="INT") }}
# after - value is already an integer
{{ outputs.fetch.value }}
Defensive patterns

Strategy: type-guard

Validate before calling

# Only stringify-and-parse when the value is not already numeric:
{% if myval is number %}
  {{ myval }}
{% else %}
  {{ myval | toString | number(type="INT") }}
{% endif %}

Type guard

# Pebble: `is number` narrows numeric types away from the string-only filter.

Prevention

When it happens

Trigger: Calling `{{ someInt | number }}` where `someInt` is already a numeric type (Integer, Long, Double); passing a Boolean; passing a parsed JSON number that Jackson already converted to a numeric type.

Common situations: Double conversion: a value is already numeric but the flow assumes it is a string; consuming a task output whose declared type is `INTEGER` and then applying `| number` redundantly.

Related errors


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