flowable/flowable-engine · error · FlowableConflictException

User '' is already part of group ''.

Error message

User '' is already part of group ''.

What it means

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).

Solutions

  1. Check membership first (GET /identity/groups/{groupId}/members) and skip if the user is present.
  2. Treat HTTP 409 as success in idempotent clients.
  3. De-duplicate the user list in batch provisioning scripts before calling the API.

Example fix

// before
members.forEach(m -> client.post("/identity/groups/sales/members", m)); // 409 on re-run
// after
Set<String> existing = fetchMemberIds("sales");
members.stream()
    .filter(m -> !existing.contains(m.getUserId()))
    .forEach(m -> client.post("/identity/groups/sales/members", m));
Defensive patterns

Strategy: validation

Validate before calling

List<String> members = client.list("/identity/groups/" + groupId + "/members")
    .stream().map(MembershipResponse::getUserId).collect(Collectors.toList());
if (members.contains(userId)) { /* skip add */ }

Try / catch

try {
    client.post("/identity/groups/" + groupId + "/members", new MembershipRequest(userId));
} catch (HttpClientErrorException e) {
    if (e.getStatusCode() == HttpStatus.CONFLICT) {
        log.info("User {} already member of {}", userId, groupId);
    } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/2ac5983acd9866be. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/identity/GroupMembershipCollectionResource.java:59

    @ApiResponses(value = {
            @ApiResponse(code = 201, message = "Indicates the group was found and the member has been added."),
            @ApiResponse(code = 400, message = "Indicates the userId was not included in the request body."),
            @ApiResponse(code = 404, message = "Indicates the requested group was not found."),
            @ApiResponse(code = 409, message = "Indicates the requested user is already a member of the group.")
    })
    @PostMapping(value = "/identity/groups/{groupId}/members", produces = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public MembershipResponse createMembership(@ApiParam(name = "groupId") @PathVariable String groupId, @RequestBody MembershipRequest memberShip) {

        Group group = getGroupFromRequest(groupId);

        if (memberShip.getUserId() == null) {
            throw new FlowableIllegalArgumentException("UserId cannot be null.");
        }

        // Check if user is member of group since API does not return typed exception
        if (identityService.createUserQuery().memberOfGroup(group.getId()).userId(memberShip.getUserId()).count() > 0) {
            throw new FlowableConflictException("User '" + memberShip.getUserId() + "' is already part of group '" + group.getId() + "'.");
        }

        identityService.createMembership(memberShip.getUserId(), group.getId());

        return restResponseFactory.createMembershipResponse(memberShip.getUserId(), group.getId());
    }
}

View on GitHub (pinned to d6d39ce1c6)