flowable/flowable-engine · error · FlowableIllegalArgumentException
FormKey expression does not resolve to a string:
Error message
FormKey expression does not resolve to a string:
What it means
The form key attribute is an expression that resolved to a non-null object which is not a String. Flowable requires the form key to be a String identifier, so it refuses to set it and throws FlowableIllegalArgumentException with the original expression text.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java:310
protected void handleFormKey(PlanItemInstanceEntity planItemInstanceEntity, ExpressionManager expressionManager,
TaskEntity taskEntity, CreateHumanTaskBeforeContext beforeContext, MigrationContext migrationContext) {
String formKeyStringValue = null;
if (migrationContext != null && migrationContext.getFormKey() != null) {
formKeyStringValue = migrationContext.getFormKey();
} else if (StringUtils.isNotEmpty(beforeContext.getFormKey())) {
formKeyStringValue = beforeContext.getFormKey();
}
if (StringUtils.isNotEmpty(formKeyStringValue)) {
Object formKey = expressionManager.createExpression(formKeyStringValue).getValue(planItemInstanceEntity);
if (formKey != null) {
if (formKey instanceof String) {
taskEntity.setFormKey((String) formKey);
} else {
throw new FlowableIllegalArgumentException("FormKey expression does not resolve to a string: " + beforeContext.getFormKey());
}
}
}
}
protected void handleDueDate(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity,
ExpressionManager expressionManager, TaskEntity taskEntity, CreateHumanTaskBeforeContext beforeContext, MigrationContext migrationContext) {
String dueDateStringValue = null;
if (migrationContext != null && migrationContext.getDueDate() != null) {
dueDateStringValue = migrationContext.getDueDate();
} else if (StringUtils.isNotEmpty(beforeContext.getDueDate())) {
dueDateStringValue = beforeContext.getDueDate();
}
if (StringUtils.isNotEmpty(dueDateStringValue)) {
Object dueDate = expressionManager.createExpression(dueDateStringValue).getValue(planItemInstanceEntity);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Make the expression resolve to a String, e.g. ${form.id.toString()} or reference the form name directly.
- Set the variable as a String when it is created.
- If the form key is static, use a literal value without ${...}.
- Catch FlowableIllegalArgumentException around plan item execution and log beforeContext.getFormKey().
Example fix
// before
<cmmn:formKey>${formId}</cmmn:formKey>
// after
<cmmn:formKey>${formId.toString()}</cmmn:formKey> Defensive patterns
Strategy: type-guard
Validate before calling
Object fk = expressionManager.createExpression(formKeyExpr).getValue(scope);
if (fk != null && !(fk instanceof String)) throw new IllegalArgumentException("formKey must resolve to String, got " + fk.getClass()); Type guard
boolean isValidFormKey(Object v) { return v == null || v instanceof String; } Try / catch
try {
// start human task plan item
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().startsWith("FormKey expression")) {
log.error("Form key expression '{}' must yield a String", e.getMessage());
}
} Prevention
- Keep form keys as String identifiers end to end
- Use ${id.toString()} when the source value is numeric
- Prefer literal form keys over expressions when the form is fixed
When it happens
Trigger: HumanTaskActivityBehavior.execute -> handleFormKey, when cmmn:formKey contains an expression whose value is a non-String, non-null object.
Common situations: Form key expression referencing a numeric form id (Long/Integer); a variable holding a JSON object or Map instead of the form name; bean method returning a FormInfo-like object rather than its key.
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
- name expression does not resolve to a string:
- documentation expression does not resolve to a string:
- Priority expression does not resolve to a number:
- Due date expression does not resolve to a Date, Instant, Loc
- Category expression does not resolve to a string:
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ae35c4e205e6c69e.
Report an issue: GitHub.