flowable/flowable-engine · error · FlowableIllegalArgumentException
Can only use a collection of String elements for referencing
Error message
Can only use a collection of String elements for referencing channel model key
What it means
A send-event plan item may define its channel keys via an expression resolving to a collection. getChannelModels() accepts only a collection whose elements are all Strings (each possibly a comma-separated list); any non-String element causes FlowableIllegalArgumentException.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/SendEventActivityBehavior.java:119
List<String> channelKeys = new ArrayList<>();
Map<String, List<ExtensionElement>> extensionElements = planItemInstanceEntity.getPlanItem().getPlanItemDefinition().getExtensionElements();
if (extensionElements != null) {
List<ExtensionElement> channelKeyElements = extensionElements.get("channelKey");
if (channelKeyElements != null && !channelKeyElements.isEmpty()) {
String channelKey = channelKeyElements.get(0).getElementText();
if (StringUtils.isNotEmpty(channelKey)) {
ExpressionManager expressionManager = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getExpressionManager();
Expression expression = expressionManager.createExpression(channelKey);
Object resolvedChannelKey = expression.getValue(planItemInstanceEntity);
if (resolvedChannelKey instanceof Collection) {
for (Object next : (Collection) resolvedChannelKey) {
if (next instanceof String) {
String[] keys = ((String) next).split(",");
channelKeys.addAll(Arrays.asList(keys));
} else {
throw new FlowableIllegalArgumentException("Can only use a collection of String elements for referencing channel model key");
}
}
} else if (resolvedChannelKey instanceof String) {
String[] keys = ((String) resolvedChannelKey).split(",");
channelKeys.addAll(Arrays.asList(keys));
}
}
}
}
if (channelKeys.isEmpty()) {
if (!sendOnSystemChannel) {
// If the event is going to be send on the system channel then it is allowed to not define any other channels
throw new FlowableException("No channel keys configured for " + planItemInstanceEntity);
} else {View on GitHub (pinned to d6d39ce1c6)
Solutions
- Ensure the expression resolves to Collection<String> — convert in the producing code (e.g. stream().map(String::valueOf).collect(...))
- Change the variable to a comma-separated String instead of a collection, which the parser also accepts and splits on commas
- Wrap the expression to convert, e.g. ${channels.toString()} only if format matches, or use a dedicated delegate that maps to strings
- Validate variable types at the point they are set on the case instance
Example fix
// before
List<Integer> channels = Arrays.asList(1, 2);
caseService.setVariable(caseInstanceId, "channelKeys", channels); // non-String elements
// after
List<String> channels = Arrays.asList("emailChannel", "smsChannel");
caseService.setVariable(caseInstanceId, "channelKeys", channels); Defensive patterns
Strategy: validation
Validate before calling
Object v = caseService.getVariable(caseInstanceId, "channelKeys");
if (v instanceof Collection) {
for (Object o : (Collection<?>) v) {
if (!(o instanceof String)) {
throw new IllegalArgumentException("channelKeys must be a Collection<String>");
}
}
} Type guard
function isStringCollection(v) { return v instanceof Collection && ((Collection) v).stream().allMatch(o -> o instanceof String); } Try / catch
try {
// trigger send-event plan item
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().contains("collection of String elements")) {
// fix variable type at the producer side
}
} Prevention
- Only set String collections (or comma-separated Strings) for channel key variables
- Convert numeric/enum keys to String before setting case variables
- Type-check channel variables where they are produced
- Document the expected channelKeys variable type in the case model
When it happens
Trigger: The channel key expression (channel key/extension element) evaluates to a Collection containing non-String objects (e.g. Integers, domain objects) while execute processes channel models.
Common situations: Expression like ${myChannelKeys} returning List<Integer> or a list of enums; variables set from other services holding non-string types; channel keys stored in a case variable with the wrong type.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Could not resolve key from expression:
- Error converting process reference expression
- Could not resolve key from expression: {eventType}
- Unable to resolve expression value for ${value} in ${planIte
- Could not execute decision: no externalRef defined for ${pla
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/d8ead7d21d9dc7c6.
Report an issue: GitHub.