flowable/flowable-engine · error · ActivitiIllegalArgumentException
FormKey expression does not resolve to a string
Error message
FormKey expression does not resolve to a string: %s
What it means
Thrown when the formKey expression of a user task (activiti:formKey) evaluates to a non-null value that is not a String during UserTaskActivityBehavior.execute(). The engine requires the form key to be a String and includes the raw expression text in the message. Null results are tolerated; any other type is not.
Solutions
- Coerce the expression to a String, e.g. ${formId.toString()}
- Change the process variable or bean getter referenced by the expression to return String
- Prefix the value or use string concatenation so the expression evaluates to a String
- Remove the formKey expression if a form key is not needed
Example fix
// before
<userTask id="approve" activiti:formKey="${formNumber}" />
// after
<userTask id="approve" activiti:formKey="${formNumber.toString()}" /> Defensive patterns
Strategy: validation
Validate before calling
Object v = ((ExecutionEntity) execution).getVariable("formId");
if (v != null && !(v instanceof String)) {
throw new IllegalArgumentException("formId must be a String, got " + v.getClass());
} Type guard
if (formKey instanceof String) { task.setFormKey((String) formKey); } Try / catch
try {
runtimeService.startProcessInstanceByKey(key, vars);
} catch (ActivitiIllegalArgumentException e) {
if (e.getMessage().startsWith("FormKey expression")) { /* coerce variable */ }
throw e;
} Prevention
- Keep form keys as String variables/attributes in the process model
- Use ${value.toString()} when referencing numeric ids in formKey
- Validate expression return types with a startup/model test
When it happens
Trigger: Starting/reaching a user task whose formKey attribute holds an expression like ${formId} where formId is a Long/Integer variable or a bean method returning a non-String.
Common situations: Numeric form keys defined as numeric process variables; a form key expression referencing a counter or enum constant; copy-pasted form key referencing the wrong variable type after refactoring.
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
- Category expression does not resolve to a string
- ' didn't resolve to a Collection
- completionCondition ' ' does not evaluate to a boolean value
- Could not resolve loopCardinality expression
- Couldn't resolve collection expression
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/c7cde5ab93ef516b.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java:206
if (activeCategoryExpression != null) {
final Object category = activeCategoryExpression.getValue(execution);
if (category != null) {
if (category instanceof String) {
task.setCategory((String) category);
} else {
throw new ActivitiIllegalArgumentException("Category expression does not resolve to a string: " +
activeCategoryExpression.getExpressionText());
}
}
}
if (activeFormKeyExpression != null) {
final Object formKey = activeFormKeyExpression.getValue(execution);
if (formKey != null) {
if (formKey instanceof String) {
task.setFormKey((String) formKey);
} else {
throw new ActivitiIllegalArgumentException("FormKey expression does not resolve to a string: " +
activeFormKeyExpression.getExpressionText());
}
}
}
if (!skipUserTask) {
handleAssignments(activeAssigneeExpression, activeOwnerExpression, activeCandidateUserExpressions,
activeCandidateGroupExpressions, task, activityExecution);
task.fireEvent(TaskListener.EVENTNAME_CREATE);
// All properties set, now firing 'create' events
if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
ActivitiEventBuilder.createEntityEvent(FlowableEngineEventType.TASK_CREATED, task),
EngineConfigurationConstants.KEY_PROCESS_ENGINE_CONFIG);
}
}View on GitHub (pinned to d6d39ce1c6)