flowable/flowable-engine · error · FlowableException
Expression
Error message
Expression
What it means
After creating the external worker job, the topic expression is evaluated at runtime; the result must be a non-empty string to become the job's handler configuration. If the expression evaluates to null or the empty string, the engine throws this FlowableException naming the expression, the value, and the plan item.
Source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/ExternalWorkerTaskActivityBehavior.java:105
if (categoryValue != null) {
job.setCategory(categoryValue.toString());
}
}
job.setJobType(JobEntity.JOB_TYPE_EXTERNAL_WORKER);
job.setRetries(jobServiceConfiguration.getAsyncExecutorNumberOfRetries());
// Inherit tenant id (if applicable)
if (planItemInstanceEntity.getTenantId() != null) {
job.setTenantId(planItemInstanceEntity.getTenantId());
}
Expression expression = CommandContextUtil.getExpressionManager(commandContext).createExpression(jobTopicExpression);
Object expressionValue = expression.getValue(planItemInstanceEntity);
if (expressionValue != null && !expressionValue.toString().isEmpty()) {
job.setJobHandlerConfiguration(expressionValue.toString());
} else {
throw new FlowableException("Expression " + jobTopicExpression + " did not evaluate to a valid value (non empty String). Was: " + expressionValue + ". For " + planItemInstanceEntity);
}
jobService.insertExternalWorkerJob(job);
if (interceptor != null) {
interceptor.afterCreateExternalWorkerJob(new CreateCmmnExternalWorkerJobAfterContext(
serviceTask,
job,
planItemInstanceEntity
));
}
}
protected String getJobCategory(BaseElement baseElement) {
List<ExtensionElement> jobCategoryElements = baseElement.getExtensionElements().get("jobCategory");
if (jobCategoryElements != null && jobCategoryElements.size() > 0) {
return jobCategoryElements.get(0).getElementText();
}View on GitHub (pinned to d6d39ce1c6)
Solutions
- Initialize the topic variable before the external worker task activates (case start variable, form field, or preceding task).
- Correct the expression variable name in the case model to match an existing case variable.
- Provide a static topic string if the topic does not need to be dynamic.
- Add a validation/sentry so the task only activates when the topic variable is non-empty.
Example fix
// before
caseRuntimeService.setVariable(caseInstanceId, "topc", "loan-processing"); // typo -> expression ${topicName} -> null
// after
caseRuntimeService.setVariable(caseInstanceId, "topicName", "loan-processing"); Defensive patterns
Strategy: validation
Validate before calling
Object topic = caseVariables.get("topicName");
if (!(topic instanceof String s) || s.isEmpty()) {
throw new IllegalArgumentException("topicName variable must be a non-empty string before external worker task activates");
} Type guard
if (topicValue instanceof String s && !s.isEmpty()) { /* valid topic */ } Try / catch
try {
caseRuntimeService.triggerPlanItemInstance(planItemId);
} catch (FlowableException e) {
if (e.getMessage().startsWith("Expression ") && e.getMessage().contains("did not evaluate to a valid value")) {
log.error("Topic expression failed for case {}", caseInstanceId);
}
throw e;
} Prevention
- Initialize topic variables at case start
- Prefer static topics when the value does not vary per instance
- Type-guard dynamic topics: keep them as String variables only
When it happens
Trigger: Expression jobTopicExpression (e.g. ${topicName}) evaluates to null, empty string, or an object whose toString() is empty when the external worker task plan item executes.
Common situations: Case variable holding the topic not set before activation; expression resolves to a null-valued data object field; variable name typo; variable set only for some case instances (per-tenant variation).
Related errors
- Unable to resolve expression value for ${value} in ${planIte
- Could not execute decision: no externalRef defined for ${pla
- Could not resolve key from expression:
- Error converting process reference expression
- Could not resolve key from expression: {eventType}
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/efb9e13b4d58d84d.
Report an issue: GitHub.