flowable/flowable-engine · error · FlowableException

Could not resolve key from expression: {eventType}

Error message

Could not resolve key from expression: {eventType}

What it means

Flowable's CMMN engine treats an event type string that starts with '{' as an expression. resolveEventDefinitionKey evaluates that expression against the plan item instance to obtain the event definition key; if the expression evaluates to null, the engine cannot route the event, so it throws a FlowableException.

Source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/operation/AbstractMovePlanItemInstanceToTerminalStateOperation.java:207

        }
    }

    protected boolean isAsyncLeave(PlanItemInstanceEntity planItemInstanceEntity, PlanItemDefinition planItemDefinition) {
        return !PlanItemInstanceState.ASYNC_ACTIVE_LEAVE.equals(planItemInstanceEntity.getState())
                && StateTransition.isPossible(planItemInstanceEntity, PlanItemTransition.ASYNC_LEAVE_ACTIVE)
                && planItemDefinition instanceof Task && ((Task) planItemDefinition).isAsyncLeave();
    }

    protected VariableAggregationDefinitions getVariableAggregations() {
        RepetitionRule repetitionRule = ExpressionUtil.getRepetitionRule(planItemInstanceEntity);
        return repetitionRule != null ? repetitionRule.getAggregations() : null;
    }
    
    protected String resolveEventDefinitionKey(String eventType, PlanItemInstanceEntity planItemInstanceEntity, CmmnEngineConfiguration cmmnEngineConfiguration) {
        Object key = cmmnEngineConfiguration.getExpressionManager().createExpression(eventType).getValue(planItemInstanceEntity);

        if (key == null) {
            throw new FlowableException("Could not resolve key from expression: " + eventType);
        }

        return key.toString();
    }

    /**
     * Implementing classes should be aware that unlike extending from AbstractChangePlanItemInstanceStateOperation, this
     * method will be executed just before the deleting the entity
     */
    @Override
    protected abstract void internalExecute();
    
    public boolean isRepeatingOnDelete(String originalState, String newState) {
        
        // If there are no entry criteria and the repetition rule evaluates to true: a new instance needs to be created.

        CaseInstanceEntity caseInstance = CommandContextUtil.getCaseInstanceEntityManager(commandContext).findById(planItemInstanceEntity.getCaseInstanceId());
        if (CaseInstanceState.isInTerminalState(caseInstance)) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Set the variable referenced by the expression on the case/plan item instance before the event is triggered (e.g. runtimeService.setVariable).
  2. Verify the expression syntax and variable name; correct typos (eventType vs event_type etc.).
  3. If the key should be a literal, remove the braces so the string is not treated as an expression.
  4. Add a null/empty guard in your own logic before triggering the event.

Example fix

// before: plan item eventType = "{eventTypeName}" but variable never set
caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
    .variable("eventTypeName", "myEvent")
    .start();
// after: variable guaranteed present before listener evaluation
Defensive patterns

Strategy: validation

Validate before calling

// java
Object v = runtimeService.getVariable(caseInstanceId, "eventTypeName");
if (v == null) throw new IllegalStateException("Variable for eventType expression is not set");

Try / catch

try {
    cmmnRuntimeService.triggerPlanItemInstance(id);
} catch (FlowableException e) {
    if (e.getMessage().startsWith("Could not resolve key from expression")) {
        // fix variable and retry
    }
}

Prevention

When it happens

Trigger: Configuring a plan item's triggeredByEvent / event type as an expression like ${eventTypeVariable} where the variable does not exist (or is null) on the plan item instance's variable scope when the agenda operation runs.

Common situations: Typos in the variable name inside the expression; variable set on the case instance but consulted before initialization; event source deleted the variable before the event listener fires; using literal text inside braces instead of a real variable.

Understand the failure class

Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.

Related errors


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