flowable/flowable-engine · error · FlowableIllegalArgumentException
formKey is null
Error message
formKey is null
What it means
HistoricPlanItemInstanceQuery.planItemInstanceFormKey() requires a non-null formKey string. Flowable throws FlowableIllegalArgumentException when null is passed because a null would be ambiguous with the field simply not being set, so the query API rejects it eagerly instead of generating incorrect SQL.
Solutions
- Pass the actual non-null form key string configured on the user task / form definition.
- Guard the call: only invoke planItemInstanceFormKey when the value is non-null; otherwise omit the filter.
- If searching for items without a form key is intended, apply that filter in application code after the query.
- Verify the case/user-task model actually defines a formKey if one is expected.
Example fix
// before
query.planItemInstanceFormKey(request.getFormKey());
// after
if (request.getFormKey() != null) {
query.planItemInstanceFormKey(request.getFormKey());
} Defensive patterns
Strategy: validation
Validate before calling
if (formKey == null || formKey.isEmpty()) {
throw new IllegalArgumentException("formKey must be provided");
}
query.planItemInstanceFormKey(formKey); Type guard
boolean hasFormKey(HistoricPlanItemInstance p) {
return p != null && p.getFormKey() != null;
} Try / catch
try {
query.planItemInstanceFormKey(formKey);
} catch (FlowableIllegalArgumentException e) {
log.warn("Invalid formKey filter: {}", e.getMessage());
// omit filter or surface validation error to caller
} Prevention
- Null-check formKey sources (task instances, config lookups, request params) before filtering.
- Model user tasks without form keys explicitly so callers know the value may be absent.
- Use @NotNull/@NotBlank bean validation on formKey in REST request DTOs.
- Skip optional filters instead of calling query methods with null.
When it happens
Trigger: Calling historicPlanItemInstanceQuery.planItemInstanceFormKey(null), e.g. planItemInstanceFormKey(task.getFormKey()) where the task/form definition has no form key, or a lookup of the form key in a properties/JSON config returned null.
Common situations: Filtering historic plan items by form key when some case definitions have no form key configured; passing user task formKey from a task that was created without one; forwarding a formKey field from an API request body where the client omitted it.
Related errors
- activatedBefore is null
- assignee is null
- availableAfter is null
- availableBefore is null
- before time is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/24150c11bf3c2b52.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/history/HistoricPlanItemInstanceQueryImpl.java:333
}
@Override
public HistoricPlanItemInstanceQuery planItemInstanceExitCriterionId(String exitCriterionId) {
if (exitCriterionId == null) {
throw new FlowableIllegalArgumentException("ExitCriterionId is null");
}
if (inOrStatement) {
this.currentOrQueryObject.exitCriterionId = exitCriterionId;
} else {
this.exitCriterionId = exitCriterionId;
}
return this;
}
@Override
public HistoricPlanItemInstanceQuery planItemInstanceFormKey(String formKey) {
if (formKey == null) {
throw new FlowableIllegalArgumentException("formKey is null");
}
if (inOrStatement) {
this.currentOrQueryObject.formKey = formKey;
} else {
this.formKey = formKey;
}
return this;
}
@Override
public HistoricPlanItemInstanceQuery planItemInstanceExtraValue(String extraValue) {
if (extraValue == null) {
throw new FlowableIllegalArgumentException("extraValue is null");
}
if (inOrStatement) {
this.currentOrQueryObject.extraValue = extraValue;
} else {
this.extraValue = extraValue;View on GitHub (pinned to d6d39ce1c6)