{"record":{"id":"6f481ee155179836","repo":"flowable/flowable-engine","slug":"cannot-assign-a-userid-to-a-task-assignment-that-a","errorCode":null,"errorMessage":"Cannot assign a userId to a task assignment that already has a groupId","messagePattern":"Cannot assign a userId to a task assignment that already has a groupId","errorType":"validation","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-identitylink-service/src/main/java/org/flowable/identitylink/service/impl/persistence/entity/HistoricIdentityLinkEntityImpl.java","lineNumber":76,"sourceCode":"    @Override\n    public String getType() {\n        return type;\n    }\n\n    @Override\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    @Override\n    public String getUserId() {\n        return userId;\n    }\n\n    @Override\n    public void setUserId(String userId) {\n        if (this.groupId != null && userId != null) {\n            throw new FlowableException(\"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    @Override\n    public void setGroupId(String groupId) {\n        if (this.userId != null && groupId != null) {\n            throw new FlowableException(\"Cannot assign a groupId to a task assignment that already has a userId\");\n        }\n        this.groupId = groupId;\n    }\n\n    @Override","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-identitylink-service/src/main/java/org/flowable/identitylink/service/impl/persistence/entity/HistoricIdentityLinkEntityImpl.java#L58-L94","documentation":"HistoricIdentityLinkEntityImpl enforces the Flowable invariant that an identity link is either a user assignment or a group assignment, never both. setUserId throws this FlowableException if a groupId is already set and a non-null userId is being assigned. It protects the persistence model from ambiguous historic task assignments.","triggerScenarios":"Calling setUserId(String) on a HistoricIdentityLinkEntity whose groupId field is already non-null, e.g. after creating a candidate-group link and then trying to also set a user.","commonSituations":"Custom history/identity-link code that reuses one entity for both group and user assignment; migrating code that assumed both could be set; building historic links manually in tests.","solutions":["Create two separate identity link entities: one for the groupId and one for the userId.","Call setGroupId(null) before assigning a userId if you truly intend to switch the link type.","Refactor the assignment code to decide up front whether the assignment targets a user or a group."],"exampleFix":"// before\nlink.setGroupId(\"management\");\nlink.setUserId(\"kermit\"); // throws\n\n// after\nIdentityLinkEntity groupLink = new HistoricIdentityLinkEntityImpl();\ngroupLink.setGroupId(\"management\");\nIdentityLinkEntity userLink = new HistoricIdentityLinkEntityImpl();\nuserLink.setUserId(\"kermit\");","handlingStrategy":"validation","validationCode":"if (link.getGroupId() != null && userId != null) {\n    throw new IllegalArgumentException(\"Use a separate identity link for this user\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    link.setUserId(userId);\n} catch (FlowableException e) {\n    // create a new identity link entity for the user instead\n}","preventionTips":["Model user and group assignments as separate identity link entities from the start.","Null the opposite field before switching a link's assignment type."],"tags":["identitylink","assignment","invariant","history"],"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"}