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
- Only call processInstanceCallbackIds when the set has at least one ID; otherwise skip the filter
- Validate/sanitize the external input that supplies callback IDs before query construction
- Use the singular processInstanceCallbackId(String) variant when there is exactly one ID
- 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
- Validate external payloads supplying callback IDs before query construction
- Use the singular processInstanceCallbackId(String) when only one ID exists
- Never pass request-derived collections straight into Flowable filters
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
- Involved groups are empty
- parentScopeIds is null or empty
- rootScopeIds is null or empty
- variableNames are null or empty
- at least one of userId or groups must be provided
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)