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

  1. Make the expression resolve to a String, e.g. ${form.id.toString()} or reference the form name directly.
  2. Set the variable as a String when it is created.
  3. If the form key is static, use a literal value without ${...}.
  4. 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

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


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/ae35c4e205e6c69e. Report an issue: GitHub.