flowable/flowable-engine · error · FlowableIllegalArgumentException

exitAfter is null

Error message

exitAfter is null

What it means

Flowable throws FlowableIllegalArgumentException when planItemInstanceExitAfter(Date) is given a null Date. The query API validates each criterion argument up front, so null timestamps are rejected at build time. Null means no filter; express that by not calling the method.

Solutions

  1. Ensure a valid Date is computed before the call.
  2. Only invoke the method when the value exists.
  3. Validate and convert date inputs with explicit failure paths.
  4. Catch FlowableIllegalArgumentException during query building and surface a clear validation message.

Example fix

// before
query.planItemInstanceExitAfter(request.getExitAfter()); // possibly null
// after
Date exitAfter = request.getExitAfter();
if (exitAfter != null) {
    query.planItemInstanceExitAfter(exitAfter);
}
Defensive patterns

Strategy: validation

Validate before calling

if (exitAfter == null) {
    throw new IllegalArgumentException("exitAfter must be non-null; omit the filter instead");
}
query.planItemInstanceExitAfter(exitAfter);

Type guard

boolean hasExitAfter = (exitAfter != null);

Try / catch

try {
    query.planItemInstanceExitAfter(exitAfter);
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("exitAfter")) {
        throw new BadRequestException("exitAfter date filter must not be null");
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling createPlanItemInstanceQuery().planItemInstanceExitAfter(null), e.g. when the exit-after date comes from an unset optional field in the caller's data model.

Common situations: Optional 'exitAfter' filter not provided by the client; deserialization leaving the field null; a date-conversion helper returning null.

Related errors


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

Appendix: source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/runtime/PlanItemInstanceQueryImpl.java:636

    }

    @Override
    public PlanItemInstanceQuery planItemInstanceExitBefore(Date exitBefore) {
        if (exitBefore == null) {
            throw new FlowableIllegalArgumentException("exitBefore is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.exitBefore = exitBefore;
        } else {
            this.exitBefore = exitBefore;
        }
        return this;
    }

    @Override
    public PlanItemInstanceQuery planItemInstanceExitAfter(Date exitAfter) {
        if (exitAfter == null) {
            throw new FlowableIllegalArgumentException("exitAfter is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.exitAfter = exitAfter;
        } else {
            this.exitAfter = exitAfter;
        }
        return this;
    }

    @Override
    public PlanItemInstanceQuery planItemInstanceEndedBefore(Date endedBefore) {
        if (endedBefore == null) {
            throw new FlowableIllegalArgumentException("endedBefore is null");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.endedBefore = endedBefore;
        } else {
            this.endedBefore = endedBefore;

View on GitHub (pinned to d6d39ce1c6)