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
- Use the unquoted boolean literal: {{ render(toRender=x, recursive=false) }} not 'false'.
- 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
- Never quote boolean literals: use false, not 'false'.
- If the recursive value comes from a variable, verify its type resolves to boolean.
- Test render() calls with both recursive modes in the Kestra UI before deploying.
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
- The 'render' function expects an argument 'toRender'.
- Maximum render() nesting depth (%s) exceeded at line %s — ch
- The 'secret' function cannot be called with both 'subkey' an
- The 'secret' function expects an argument 'key'.
- The 'subflow' function expects the arguments 'namespace' and
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/c87743ab3a286c21.
Report an issue: GitHub.