kestra-io/kestra · error · PebbleException

The 'render' function expects an optional argument 'recursiv

Error message

The 'render' function expects an optional argument 'recursive' with type boolean.

What it means

The render() function accepts an optional 'recursive' argument that must be a boolean. When provided, it is checked with instanceof Boolean; if it is any other type (string, number, null from an explicit null literal in certain contexts), the function throws. Note that a null value (argument absent) defaults to true, so this only fires when the argument is present but non-boolean.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/RenderFunction.java:68

                null,
                "Maximum render() nesting depth (" + maxDepth + ") exceeded at line " + lineNumber +
                    " — check for circular render() calls in your template.",
                lineNumber, self.getName()
            );
        }

        if (!args.containsKey("toRender")) {
            throw new PebbleException(null, "The 'render' function expects an argument 'toRender'.", lineNumber, self.getName());
        }
        Object toRender = args.get("toRender");

        Object recursiveArg = args.get("recursive");
        if (recursiveArg == null) {
            recursiveArg = true;
        }

        if (!(recursiveArg instanceof Boolean recursive)) {
            throw new PebbleException(null, "The 'render' function expects an optional argument 'recursive' with type boolean.", lineNumber, self.getName());
        }

        EvaluationContextImpl evaluationContext = (EvaluationContextImpl) context;
        Map<String, Object> variables = evaluationContext.getScopeChain().getGlobalScopes().stream()
            .flatMap(scope -> scope.getKeys().stream())
            .distinct()
            .collect(HashMap::new, (m, v) -> m.put(v, context.getVariable(v)), HashMap::putAll);

        try {
            return ((RenderingFunctionInterface) evaluationContext.getExtensionRegistry().getFunction(functionName())).variableRenderer(applicationContext)
                .renderObject(toRender, variables, recursive, depth + 1).orElse(null);
        } catch (IllegalVariableEvaluationException e) {
            throw new PebbleException(e, e.getMessage());
        }
    }

    @Override
    public String functionName() {

View on GitHub (pinned to 823fada927)

Solutions

  1. Use the unquoted boolean literal: {{ render(toRender=x, recursive=false) }} not 'false'.
  2. Ensure any dynamic expression used for recursive resolves to a true Pebble boolean, not a string.

Example fix

# before — string literal
{{ render(toRender=inputs.x, recursive='false') }}

# after — boolean literal
{{ render(toRender=inputs.x, recursive=false) }}
Defensive patterns

Strategy: type-guard

Validate before calling

# Pass recursive as an unquoted boolean literal (true/false), not a string.
# Correct: {{ render(toRender=x, recursive=false) }}
# If using a dynamic value, ensure it resolves to a boolean:
{% set rec = inputs.recursive if inputs.recursive is boolean else true %}{{ render(toRender=x, recursive=rec) }}

Prevention

When it happens

Trigger: Passing recursive as a string: {{ render(toRender=x, recursive='true') }}. Passing an integer or a variable that resolves to a non-boolean type. Using a Pebble expression for recursive that evaluates to a string 'false' instead of the boolean false.

Common situations: Passing recursive=false as a string literal (quoted) vs boolean literal (unquoted). Dynamic expressions where the variable type is STRING but expected to be BOOLEAN. Copy-pasting from examples that used string values.

Related errors


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