kestra-io/kestra · error · IllegalArgumentException
Cannot concat {} with {}
Error message
Cannot concat {} with {} What it means
Thrown by TypedObjectWriter.concatSpecialized when both the current accumulated value and the incoming value are non-scalar objects (not Boolean, Character, String, or Number). The typed writer can only concatenate scalars via string join; attempting to merge two complex objects (e.g., two Maps, or a Map and a List) is not supported.
Source
Thrown at core/src/main/java/io/kestra/core/runners/pebble/TypedObjectWriter.java:67
concatSpecialized(s);
}
private void concatSpecialized(final Object o) {
if (o == null) {
return;
}
if (current == null) {
current = o;
return;
}
if (isConcatenableScalar(current) && isConcatenableScalar(o)) {
current = current.toString() + o;
return;
}
throw new IllegalArgumentException(
"Cannot concat " + current.getClass().getName() + " with " + o.getClass().getName()
);
}
@SneakyThrows
@Override
public void write(Object o) {
if (o == null) {
return;
}
if (current == null) {
current = o;
} else if (isConcatenableScalar(o)) {
// do call the writeSpecialized method depending on the object type.
SpecializedWriter.super.write(o);
} else {
throw new IllegalArgumentException(View on GitHub (pinned to 823fada927)
Solutions
- Avoid concatenating two complex objects; instead merge them or access individual scalar fields and concatenate those.
- Use the json filter or to_json to convert an object to a string before concatenating: '{{ obj1 | json ~ obj2 | json }}'.
- Restructure the template to build the desired structure with explicit map/list literals rather than concatenation.
Example fix
{# before #}
{{ outputs.task1.value ~ outputs.task2.value }}
{# after #}
{{ outputs.task1.value | json }}{{ outputs.task2.value | json }} Defensive patterns
Strategy: type-guard
Type guard
{# In Pebble, check that both values are scalars before concatenating #}
{% set a = outputs.task1.value %}
{% set b = outputs.task2.value %}
{% if a is string and b is string %}
{{ a ~ b }}
{% else %}
{{ a | json }}{{ b | json }}
{% endif %} Prevention
- Never use the tilde (~) concatenation operator on two complex objects; convert to strings first with the json filter.
- Use render() (string mode) instead of renderTyped() when type preservation is not required, as the JsonWriter handles mixed types more gracefully.
- Validate the types of task outputs before using them in concatenation expressions.
When it happens
Trigger: A Pebble template that uses concatenation ('~' operator or implicit concatenation in a typed/stringified rendering context) to join two complex, non-scalar values — e.g., '{{ obj1 ~ obj2 }}' where both are Maps or Lists. Reached during renderTyped or when the stringify fallback writer processes mixed types. This specific path is through concatSpecialized, which is called by the writeSpecialized overloads.
Common situations: Building a JSON structure in a template where two map/list fragments are concatenated instead of merged. Using the tilde operator on variables that resolve to objects rather than strings in a renderTyped context.
Related errors
- Could not perform greater than or equals comparison
- Could not perform greater than comparison
- Could not perform less than or equals comparison
- Could not perform less than comparisonPebb
- 'chunk' filter can only be applied to List. Actual type was:
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/852e5da87c3dcbf6.
Report an issue: GitHub.