flowable/flowable-engine · error · FlowableIllegalArgumentException

userId and groupId cannot both be null

Error message

userId and groupId cannot both be null

What it means

FlowableIllegalArgumentException thrown by AddIdentityLinkForProcessInstanceCmd.validateParams when both userId and groupId are null. An identity link must point at a user, a group, or both; a link to neither is meaningless so the command rejects it after the type check.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/AddIdentityLinkForProcessInstanceCmd.java:63

        validateParams(processInstanceId, userId, groupId, type);
        this.processInstanceId = processInstanceId;
        this.userId = userId;
        this.groupId = groupId;
        this.type = type;
    }

    protected void validateParams(String processInstanceId, String userId, String groupId, String type) {

        if (processInstanceId == null) {
            throw new FlowableIllegalArgumentException("processInstanceId is null");
        }

        if (type == null) {
            throw new FlowableIllegalArgumentException("type is required when adding a new process instance identity link");
        }

        if (userId == null && groupId == null) {
            throw new FlowableIllegalArgumentException("userId and groupId cannot both be null");
        }

    }

    @Override
    public Void execute(CommandContext commandContext) {

        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);
        ExecutionEntity processInstance = executionEntityManager.findById(processInstanceId);

        if (processInstance == null) {
            throw new FlowableObjectNotFoundException("Cannot find process instance with id " + processInstanceId, ExecutionEntity.class);
        }

        if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, processInstance.getProcessDefinitionId())) {
            Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
            compatibilityHandler.addIdentityLinkForProcessInstance(processInstanceId, userId, groupId, type);
            return null;

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Ensure at least one of userId or groupId is a non-null, non-empty string before calling the API.
  2. Check the upstream lookup that produces userId/groupId (it returned null) and handle the empty case earlier.
  3. If you truly want a link without user/group, use a different mechanism (e.g. process variable) — identity links require a target.

Example fix

// before
runtimeService.addIdentityLinkForProcessInstance(piId, userId, groupId, IdentityLinkType.CANDIDATE); // both null
// after
if (userId == null && groupId == null) {
    throw new IllegalArgumentException("Provide userId or groupId");
}
runtimeService.addIdentityLinkForProcessInstance(piId, userId, groupId, IdentityLinkType.CANDIDATE);
Defensive patterns

Strategy: validation

Validate before calling

if ((userId == null || userId.isEmpty()) && (groupId == null || groupId.isEmpty())) {
    throw new IllegalArgumentException("identity link requires userId or groupId");
}

Type guard

boolean hasTarget(String userId, String groupId) {
    return (userId != null && !userId.isEmpty()) || (groupId != null && !groupId.isEmpty());
}

Try / catch

try {
    runtimeService.addGroupIdentityLink(piId, groupId, IdentityLinkType.CANDIDATE);
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("cannot both be null")) {
        // resolve the user/group upstream before retrying
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling RuntimeService.addUserIdentityLink / addGroupIdentityLink / addIdentityLinkForProcessInstance with a null type-safe type but null userId and null groupId, e.g. addIdentityLinkForProcessInstance(pid, null, null, "candidate").

Common situations: Resolving assignee/group from external data (LDAP lookup, form field) that returned nothing; both userId and groupId sourced from optional request fields left empty; wiring bug passing wrong variables into the command.

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/b3b847ed838600c3. Report an issue: GitHub.