{"record":{"id":"b3b847ed838600c3","repo":"flowable/flowable-engine","slug":"userid-and-groupid-cannot-both-be-null-b3b847","errorCode":null,"errorMessage":"userId and groupId cannot both be null","messagePattern":"userId and groupId cannot both be null","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/AddIdentityLinkForProcessInstanceCmd.java","lineNumber":63,"sourceCode":"        validateParams(processInstanceId, userId, groupId, type);\n        this.processInstanceId = processInstanceId;\n        this.userId = userId;\n        this.groupId = groupId;\n        this.type = type;\n    }\n\n    protected void validateParams(String processInstanceId, String userId, String groupId, String type) {\n\n        if (processInstanceId == null) {\n            throw new FlowableIllegalArgumentException(\"processInstanceId is null\");\n        }\n\n        if (type == null) {\n            throw new FlowableIllegalArgumentException(\"type is required when adding a new process instance identity link\");\n        }\n\n        if (userId == null && groupId == null) {\n            throw new FlowableIllegalArgumentException(\"userId and groupId cannot both be null\");\n        }\n\n    }\n\n    @Override\n    public Void execute(CommandContext commandContext) {\n\n        ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(commandContext);\n        ExecutionEntity processInstance = executionEntityManager.findById(processInstanceId);\n\n        if (processInstance == null) {\n            throw new FlowableObjectNotFoundException(\"Cannot find process instance with id \" + processInstanceId, ExecutionEntity.class);\n        }\n\n        if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, processInstance.getProcessDefinitionId())) {\n            Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();\n            compatibilityHandler.addIdentityLinkForProcessInstance(processInstanceId, userId, groupId, type);\n            return null;","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/AddIdentityLinkForProcessInstanceCmd.java#L45-L81","documentation":"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.","triggerScenarios":"Calling RuntimeService.addUserIdentityLink / addGroupIdentityLink / addIdentityLinkForProcessInstance with a null type-safe type but null userId and null groupId, e.g. addIdentityLinkForProcessInstance(pid, null, null, \"candidate\").","commonSituations":"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.","solutions":["Ensure at least one of userId or groupId is a non-null, non-empty string before calling the API.","Check the upstream lookup that produces userId/groupId (it returned null) and handle the empty case earlier.","If you truly want a link without user/group, use a different mechanism (e.g. process variable) — identity links require a target."],"exampleFix":"// before\nruntimeService.addIdentityLinkForProcessInstance(piId, userId, groupId, IdentityLinkType.CANDIDATE); // both null\n// after\nif (userId == null && groupId == null) {\n    throw new IllegalArgumentException(\"Provide userId or groupId\");\n}\nruntimeService.addIdentityLinkForProcessInstance(piId, userId, groupId, IdentityLinkType.CANDIDATE);","handlingStrategy":"validation","validationCode":"if ((userId == null || userId.isEmpty()) && (groupId == null || groupId.isEmpty())) {\n    throw new IllegalArgumentException(\"identity link requires userId or groupId\");\n}","typeGuard":"boolean hasTarget(String userId, String groupId) {\n    return (userId != null && !userId.isEmpty()) || (groupId != null && !groupId.isEmpty());\n}","tryCatchPattern":"try {\n    runtimeService.addGroupIdentityLink(piId, groupId, IdentityLinkType.CANDIDATE);\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().contains(\"cannot both be null\")) {\n        // resolve the user/group upstream before retrying\n    }\n    throw e;\n}","preventionTips":["Resolve userId/groupId from lookups that fail loudly instead of returning null.","Make userId or groupId a required field in the API that triggers identity-link creation.","Prefer using addUserIdentityLink or addGroupIdentityLink so only one target can be passed."],"tags":["flowable","identity-link","null-argument","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}