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

  1. Pass the actual non-null form key string configured on the user task / form definition.
  2. Guard the call: only invoke planItemInstanceFormKey when the value is non-null; otherwise omit the filter.
  3. If searching for items without a form key is intended, apply that filter in application code after the query.
  4. 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

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


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)