flowable/flowable-engine · error · FlowableIllegalArgumentException

callbackIds is null or empty

Error message

callbackIds is null or empty

What it means

ProcessInstanceQueryImpl.processInstanceCallbackIds(Set<String>) filters process instances by callback IDs (used with process/callback-type correlations). Flowable throws FlowableIllegalArgumentException when the set is null or empty because an empty IN clause is invalid SQL and the filter would be meaningless. The check runs in the query method itself.

Solutions

  1. Only call processInstanceCallbackIds when the set has at least one ID; otherwise skip the filter
  2. Validate/sanitize the external input that supplies callback IDs before query construction
  3. Use the singular processInstanceCallbackId(String) variant when there is exactly one ID
  4. Catch FlowableIllegalArgumentException and respond with a clear client-side validation error

Example fix

// before
query.processInstanceCallbackIds(callbackIds); // may be empty
// after
if (callbackIds != null && !callbackIds.isEmpty()) {
    query.processInstanceCallbackIds(callbackIds);
} else {
    throw new IllegalArgumentException("At least one callback id must be provided");
}
Defensive patterns

Strategy: validation

Validate before calling

if (callbackIds == null || callbackIds.isEmpty()) {
    throw new IllegalArgumentException("At least one callback id is required");
}

Type guard

boolean hasCallbackIds = callbackIds != null && !callbackIds.isEmpty();

Try / catch

try {
    query.processInstanceCallbackIds(callbackIds);
} catch (FlowableIllegalArgumentException e) {
    // convert to client-side validation error
}

Prevention

When it happens

Trigger: Calling processInstanceCallbackIds(null) or an empty set, typically when callback IDs were supplied by external callers (e.g., REST payloads) that contained none.

Common situations: REST/connector integration where the callback identifier list arrives empty from the client; business logic that only sometimes populates callback IDs but always applies the filter; copy-paste from processInstanceCallbackId (singular).

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/ProcessInstanceQueryImpl.java:784

            this.activeActivityIds = activityIds;
        }
        return this;
    }

    @Override
    public ProcessInstanceQuery processInstanceCallbackId(String callbackId) {
        if (inOrStatement) {
            this.currentOrQueryObject.callbackId = callbackId;
        } else {
            this.callbackId = callbackId;
        }
        return this;
    }

    @Override
    public ProcessInstanceQuery processInstanceCallbackIds(Set<String> callbackIds) {
        if (callbackIds == null || callbackIds.isEmpty()) {
            throw new FlowableIllegalArgumentException("callbackIds is null or empty");
        }
        if (inOrStatement) {
            this.currentOrQueryObject.callbackIds = callbackIds;
        } else {
            this.callbackIds = callbackIds;
        }
        return this;
    }
    
    @Override
    public ProcessInstanceQuery processInstanceCallbackType(String callbackType) {
        if (inOrStatement) {
            this.currentOrQueryObject.callbackType = callbackType;
        } else {
            this.callbackType = callbackType;
        }
        return this;
    }

View on GitHub (pinned to d6d39ce1c6)