flowable/flowable-engine · error · FlowableIllegalArgumentException
Incompatible usage: cannot use type
Error message
Incompatible usage: cannot use type '${type}' together with a groupId What it means
DeleteIdentityLinkCmd rejects semantically invalid combinations: ASSIGNEE and OWNER links apply to a single user, so a groupId is incompatible; for all other types, at least one of userId or groupId must be provided. Violations throw FlowableIllegalArgumentException.
Solutions
- For ASSIGNEE/OWNER types pass only a userId and null groupId
- For other types ensure exactly the relevant id (userId or groupId) is non-null
- Split the removal logic into user-link and group-link branches instead of one generic call
Example fix
// before taskService.deleteGroupIdentityLink(taskId, groupId, IdentityLinkType.ASSIGNEE); // incompatible // after taskService.deleteUserIdentityLink(taskId, userId, IdentityLinkType.ASSIGNEE);
Defensive patterns
Strategy: validation
Validate before calling
boolean valid;
if (IdentityLinkType.ASSIGNEE.equals(type) || IdentityLinkType.OWNER.equals(type)) {
valid = groupId == null;
} else {
valid = userId != null || groupId != null;
}
if (!valid) {
throw new IllegalArgumentException("Invalid identity link argument combination for type " + type);
} Type guard
boolean isValidLinkCombo = (type, userId, groupId) ->
(IdentityLinkType.ASSIGNEE.equals(type) || IdentityLinkType.OWNER.equals(type))
? groupId == null
: userId != null || groupId != null; Try / catch
try {
taskService.deleteGroupIdentityLink(taskId, groupId, type);
} catch (FlowableIllegalArgumentException e) {
logger.warn("Incompatible identity link arguments", e);
} Prevention
- For ASSIGNEE/OWNER always use userId-only APIs
- Keep user-link and group-link removal in separate code paths
- Mirror the engine's validateParams rules in your own helper layer
When it happens
Trigger: deleteUserIdentityLink/deleteGroupIdentityLink calls where type is ASSIGNEE or OWNER but a groupId is passed, or where both userId and groupId are null for any other type.
Common situations: Copy-paste between user and group link APIs, generic link-removal helpers that pass both userId/groupId through unchecked, or code computing type dynamically (assignee) while still supplying a group id.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- A group or a user is required to create an identity link.
- Only one of user or group can be used to create an identity…
- type is required when adding a new case instance identity…
- type is required when adding a new process instance…
- userId and groupId cannot both be null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/eae430ce7b3112fa.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/DeleteIdentityLinkCmd.java:65
this.taskId = taskId;
this.userId = userId;
this.groupId = groupId;
this.type = type;
}
protected void validateParams(String userId, String groupId, String type, String taskId) {
if (taskId == null) {
throw new FlowableIllegalArgumentException("taskId is null");
}
if (type == null) {
throw new FlowableIllegalArgumentException("type is required when adding a new task identity link");
}
// Special treatment for assignee and owner: group cannot be used and userId may be null
if (IdentityLinkType.ASSIGNEE.equals(type) || IdentityLinkType.OWNER.equals(type)) {
if (groupId != null) {
throw new FlowableIllegalArgumentException("Incompatible usage: cannot use type '" + type + "' together with a groupId");
}
} else {
if (userId == null && groupId == null) {
throw new FlowableIllegalArgumentException("userId and groupId cannot both be null");
}
}
}
@Override
protected Void execute(CommandContext commandContext, TaskEntity task) {
if (task.getProcessDefinitionId() != null && Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, task.getProcessDefinitionId())) {
Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
compatibilityHandler.deleteIdentityLink(taskId, userId, groupId, type);
return null;
}
if (IdentityLinkType.ASSIGNEE.equals(type)) {
TaskHelper.changeTaskAssignee(task, null);View on GitHub (pinned to d6d39ce1c6)