{"record":{"id":"b17d1cd0f8aa84c1","repo":"flowable/flowable-engine","slug":"cannot-assign-a-groupid-to-a-task-assignment-that-b17d1c","errorCode":null,"errorMessage":"Cannot assign a groupId to a task assignment that already has a userId","messagePattern":"Cannot assign a groupId to a task assignment that already has a userId","errorType":"validation","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/HistoricIdentityLinkEntity.java","lineNumber":127,"sourceCode":"    public String getUserId() {\n        return userId;\n    }\n\n    public void setUserId(String userId) {\n        if (this.groupId != null && userId != null) {\n            throw new ActivitiException(\"Cannot assign a userId to a task assignment that already has a groupId\");\n        }\n        this.userId = userId;\n    }\n\n    @Override\n    public String getGroupId() {\n        return groupId;\n    }\n\n    public void setGroupId(String groupId) {\n        if (this.userId != null && groupId != null) {\n            throw new ActivitiException(\"Cannot assign a groupId to a task assignment that already has a userId\");\n        }\n        this.groupId = groupId;\n    }\n\n    @Override\n    public String getTaskId() {\n        return taskId;\n    }\n\n    public void setTaskId(String taskId) {\n        this.taskId = taskId;\n    }\n\n    @Override\n    public String getProcessInstanceId() {\n        return processInstanceId;\n    }\n","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/persistence/entity/HistoricIdentityLinkEntity.java#L109-L145","documentation":"Mirror of setUserId: a historic identity link is either a user assignment or a group assignment. setGroupId throws when a userId is already set and a non-null groupId is assigned, preventing a single row from representing both.","triggerScenarios":"Calling setGroupId on a HistoricIdentityLinkEntity whose userId is already non-null with a non-null groupId.","commonSituations":"Same as the userId variant: entity reuse in custom history handling, migration scripts, or event listeners copying assignment data without clearing the prior field.","solutions":["Use a fresh entity for the group assignment.","Set userId to null first if the link must become group-based.","Model candidate user + candidate group as two separate identity link records."],"exampleFix":"// before\nlink.setUserId(\"kermit\");\nlink.setGroupId(\"management\"); // throws\n\n// after\nlink.setUserId(\"kermit\");\nHistoricIdentityLinkEntity groupLink = ...; groupLink.setGroupId(\"management\");","handlingStrategy":"type-guard","validationCode":"if (link.getUserId() != null && groupId != null) {\n    throw new IllegalArgumentException(\"link already user-based\");\n}","typeGuard":"function canSetGroupId(link) {\n  return link.getUserId() == null;\n}","tryCatchPattern":"try {\n    link.setGroupId(groupId);\n} catch (ActivitiException e) {\n    link = createNewIdentityLink();\n    link.setGroupId(groupId);\n}","preventionTips":["Keep candidate user/group links separate from assignee links","Prefer the public taskService API over direct entity manipulation","Map external 'user or group' fields to two distinct links"],"tags":["identity-link","history","assignment"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}