flowable/flowable-engine · error · FlowableIllegalArgumentException

userId and groupId cannot both be null

Error message

userId and groupId cannot both be null

What it means

DeleteIdentityLinkCmd requires at least one identity to act on. Flowable throws this FlowableIllegalArgumentException during validateParams when both userId and groupId are null and the identity link type is not ASSIGNEE/OWNER, since there would be no identity link to delete.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/DeleteIdentityLinkCmd.java:69

    }

    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);
        } else if (IdentityLinkType.OWNER.equals(type)) {
            TaskHelper.changeTaskOwner(task, null);
        } else {
            IdentityLinkUtil.deleteTaskIdentityLinks(task, userId, groupId, type);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Supply either userId or groupId (one may be null, but not both) in the delete call.
  2. If the intent was to remove an assignee/owner, pass the user id and type ASSIGNEE or OWNER.
  3. Guard the call site: skip the API call when both values are null, or reject the input earlier.

Example fix

// before
identityService.deleteTaskIdentityLink(taskId, null, null, IdentityLinkType.CANDIDATE);
// after
if (userId != null || groupId != null) {
    identityService.deleteTaskIdentityLink(taskId, userId, groupId, IdentityLinkType.CANDIDATE);
}
Defensive patterns

Strategy: validation

Validate before calling

// Java
if (userId == null && groupId == null) {
    throw new IllegalArgumentException("Provide userId or groupId before deleting the identity link");
}
identityService.deleteTaskIdentityLink(taskId, userId, groupId, type);

Type guard

boolean hasIdentity(String userId, String groupId) { return userId != null || groupId != null; }

Try / catch

try {
    identityService.deleteTaskIdentityLink(taskId, userId, groupId, type);
} catch (FlowableIllegalArgumentException e) {
    log.warn("Invalid identity link delete request: {}", e.getMessage());
}

Prevention

When it happens

Trigger: Calling IdentityService.deleteTaskIdentityLink(taskId, null, null, type) or RuntimeService.deleteProcessInstanceIdentityLink(processInstanceId, null, null, type) with both identity arguments null for a non-assignee/owner type (e.g. 'candidate').

Common situations: Passing optional variables from request payloads where neither user nor group was provided; forgetting that 'candidate' links are either user or group scoped; copying a delete call from an add call but clearing both ids.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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