flowable/flowable-engine · error · FlowableIllegalArgumentException
completedAfter is null
Error message
completedAfter is null
What it means
Flowable throws FlowableIllegalArgumentException when planItemInstanceCompletedAfter(Date) is called with a null Date argument. Query parameter methods validate their inputs eagerly so an invalid query fails at construction time rather than producing a broken SQL query or silent wrong results. Passing null means the caller has no completedAfter timestamp to filter on and should simply not call the method.
Solutions
- Ensure a non-null Date is passed: resolve the value from user input or compute a default before calling planItemInstanceCompletedAfter.
- Only call the method when the value exists (null-check at the call site); omitting the call means no filter on this attribute.
- If input is a string, parse it with a checked date parser and reject invalid/empty input before building the query.
- Wrap the query-building code in a try-catch for FlowableIllegalArgumentException to fail fast with a clear client-facing message.
Example fix
// before
Date completedAfter = params.get("completedAfter"); // may be null
query.planItemInstanceCompletedAfter(completedAfter);
// after
Date completedAfter = params.get("completedAfter");
if (completedAfter != null) {
query.planItemInstanceCompletedAfter(completedAfter);
} Defensive patterns
Strategy: validation
Validate before calling
if (completedAfter == null) {
throw new IllegalArgumentException("completedAfter must be provided or the filter must be omitted");
}
query.planItemInstanceCompletedAfter(completedAfter); Type guard
boolean hasCompletedAfter = (completedAfter != null);
Try / catch
try {
query.planItemInstanceCompletedAfter(completedAfter);
} catch (FlowableIllegalArgumentException e) {
if (e.getMessage().contains("completedAfter")) {
throw new BadRequestException("completedAfter date filter must not be null");
}
throw e;
} Prevention
- Null-check optional dates at the call site and conditionally apply query criteria
- Validate/parse user-supplied date strings before building queries
- Use Optional<Date> in your service layer to make absence explicit
- Centralize query building in a helper that only applies non-null filters
When it happens
Trigger: Calling runtimeService.createPlanItemInstanceQuery().planItemInstanceCompletedAfter(null), typically when the date comes from an optional request parameter or deserialized DTO field that was not set.
Common situations: REST/UI layer passes through an unvalidated 'completedAfter' query param that is absent or null; a Java bean built from JSON where the field was omitted; a default-initialization bug where a Calendar/Date conversion returned null.
Related errors
- activatedBefore is null
- activity tenant id is null
- after time is null
- category is null
- categoryLike is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/ec9911bef20e89ee.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceQueryImpl.java:532
}
@Override
public PlanItemInstanceQuery planItemInstanceCompletedBefore(Date completedBefore) {
if (completedBefore == null) {
throw new FlowableIllegalArgumentException("completedBefore is null");
}
if (inOrStatement) {
this.currentOrQueryObject.completedBefore = completedBefore;
} else {
this.completedBefore = completedBefore;
}
return this;
}
@Override
public PlanItemInstanceQuery planItemInstanceCompletedAfter(Date completedAfter) {
if (completedAfter == null) {
throw new FlowableIllegalArgumentException("completedAfter is null");
}
if (inOrStatement) {
this.currentOrQueryObject.completedAfter = completedAfter;
} else {
this.completedAfter = completedAfter;
}
return this;
}
@Override
public PlanItemInstanceQuery planItemInstanceOccurredBefore(Date occurredBefore) {
if (occurredBefore == null) {
throw new FlowableIllegalArgumentException("occurredBefore is null");
}
if (inOrStatement) {
this.currentOrQueryObject.occurredBefore = occurredBefore;
} else {
this.occurredBefore = occurredBefore;View on GitHub (pinned to d6d39ce1c6)