{"record":{"id":"2ac5983acd9866be","repo":"flowable/flowable-engine","slug":"user-is-already-part-of-group","errorCode":null,"errorMessage":"User '' is already part of group ''.","messagePattern":"User '' is already part of group ''\\.","errorType":"http","errorClass":"FlowableConflictException","httpStatus":409,"severity":"error","filePath":"modules/flowable-rest/src/main/java/org/flowable/rest/service/api/identity/GroupMembershipCollectionResource.java","lineNumber":59,"sourceCode":"    @ApiResponses(value = {\n            @ApiResponse(code = 201, message = \"Indicates the group was found and the member has been added.\"),\n            @ApiResponse(code = 400, message = \"Indicates the userId was not included in the request body.\"),\n            @ApiResponse(code = 404, message = \"Indicates the requested group was not found.\"),\n            @ApiResponse(code = 409, message = \"Indicates the requested user is already a member of the group.\")\n    })\n    @PostMapping(value = \"/identity/groups/{groupId}/members\", produces = \"application/json\")\n    @ResponseStatus(HttpStatus.CREATED)\n    public MembershipResponse createMembership(@ApiParam(name = \"groupId\") @PathVariable String groupId, @RequestBody MembershipRequest memberShip) {\n\n        Group group = getGroupFromRequest(groupId);\n\n        if (memberShip.getUserId() == null) {\n            throw new FlowableIllegalArgumentException(\"UserId cannot be null.\");\n        }\n\n        // Check if user is member of group since API does not return typed exception\n        if (identityService.createUserQuery().memberOfGroup(group.getId()).userId(memberShip.getUserId()).count() > 0) {\n            throw new FlowableConflictException(\"User '\" + memberShip.getUserId() + \"' is already part of group '\" + group.getId() + \"'.\");\n        }\n\n        identityService.createMembership(memberShip.getUserId(), group.getId());\n\n        return restResponseFactory.createMembershipResponse(memberShip.getUserId(), group.getId());\n    }\n}\n","sourceCodeStart":41,"sourceCodeEnd":67,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-rest/src/main/java/org/flowable/rest/service/api/identity/GroupMembershipCollectionResource.java#L41-L67","documentation":"createMembership first checks via createUserQuery().memberOfGroup(groupId).userId(userId).count() whether the user is already a member, since the underlying identity API does not throw a typed exception for duplicates. If the count is positive it throws FlowableConflictException (HTTP 409).","triggerScenarios":"POST /identity/groups/{groupId}/members where userId is already a member of that group, e.g. retrying a successful membership add or a double-submitted form.","commonSituations":"Idempotency gaps in provisioning scripts that add users to groups on every run; double-click/double-submit on an 'add member' UI; synchronization job re-adding existing memberships.","solutions":["Check membership first (GET /identity/groups/{groupId}/members) and skip if the user is present.","Treat HTTP 409 as success in idempotent clients.","De-duplicate the user list in batch provisioning scripts before calling the API."],"exampleFix":"// before\nmembers.forEach(m -> client.post(\"/identity/groups/sales/members\", m)); // 409 on re-run\n// after\nSet<String> existing = fetchMemberIds(\"sales\");\nmembers.stream()\n    .filter(m -> !existing.contains(m.getUserId()))\n    .forEach(m -> client.post(\"/identity/groups/sales/members\", m));","handlingStrategy":"validation","validationCode":"List<String> members = client.list(\"/identity/groups/\" + groupId + \"/members\")\n    .stream().map(MembershipResponse::getUserId).collect(Collectors.toList());\nif (members.contains(userId)) { /* skip add */ }","typeGuard":null,"tryCatchPattern":"try {\n    client.post(\"/identity/groups/\" + groupId + \"/members\", new MembershipRequest(userId));\n} catch (HttpClientErrorException e) {\n    if (e.getStatusCode() == HttpStatus.CONFLICT) {\n        log.info(\"User {} already member of {}\", userId, groupId);\n    } else throw e;\n}","preventionTips":["Treat 409 as success in idempotent provisioning.","De-duplicate member lists before batch adds.","Guard double-submits in the UI."],"tags":["rest-api","conflict","membership","duplicate"],"backgroundTag":"duplicate-record-conflict","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"}