{"record":{"id":"324a478d0eb76af7","repo":"flowable/flowable-engine","slug":"groupid-is-null-324a47","errorCode":null,"errorMessage":"groupId is null","messagePattern":"groupId is null","errorType":"validation","errorClass":"FlowableIllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/flowable-idm-engine/src/main/java/org/flowable/idm/engine/impl/cmd/DeleteMembershipCmd.java","lineNumber":42,"sourceCode":" */\npublic class DeleteMembershipCmd implements Command<Void>, Serializable {\n\n    private static final long serialVersionUID = 1L;\n    String userId;\n    String groupId;\n\n    public DeleteMembershipCmd(String userId, String groupId) {\n        this.userId = userId;\n        this.groupId = groupId;\n    }\n\n    @Override\n    public Void execute(CommandContext commandContext) {\n        if (userId == null) {\n            throw new FlowableIllegalArgumentException(\"userId is null\");\n        }\n        if (groupId == null) {\n            throw new FlowableIllegalArgumentException(\"groupId is null\");\n        }\n\n        CommandContextUtil.getMembershipEntityManager(commandContext).deleteMembership(userId, groupId);\n\n        return null;\n    }\n\n}\n","sourceCodeStart":24,"sourceCodeEnd":51,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-idm-engine/src/main/java/org/flowable/idm/engine/impl/cmd/DeleteMembershipCmd.java#L24-L51","documentation":"DeleteMembershipCmd.execute() validates both membership key components; this error is raised when groupId is null (after the userId check passed). A membership cannot be located without both userId and groupId.","triggerScenarios":"Calling IdentityService.deleteMembership(userId, null), or new DeleteMembershipCmd(userId, null); typically when the group id comes from a lookup that returned nothing or an unset form field.","commonSituations":"Removing a user from a group whose id was resolved by name and the name lookup failed; admin UI sending only userId; refactoring where parameter order was changed and groupId fell through as null.","solutions":["Pass a non-null groupId to IdentityService.deleteMembership().","Resolve the group by name first and fail with a clear 'group not found' error if the lookup yields null.","Validate both parameters at the service/UI boundary before calling the engine.","Catch FlowableIllegalArgumentException and map it to a 400-style validation response."],"exampleFix":"// before\nidentityService.deleteMembership(userId, groupId); // groupId may be null\n\n// after\nObjects.requireNonNull(groupId, \"groupId is required to delete a membership\");\nidentityService.deleteMembership(userId, groupId);","handlingStrategy":"validation","validationCode":"if (groupId == null) {\n    throw new IllegalArgumentException(\"groupId must be provided before deleting a membership\");\n}\nif (userId == null) {\n    throw new IllegalArgumentException(\"userId must be provided before deleting a membership\");\n}","typeGuard":"boolean hasValidMembershipKey(String userId, String groupId) {\n    return userId != null && groupId != null;\n}","tryCatchPattern":"try {\n    identityService.deleteMembership(userId, groupId);\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().contains(\"groupId is null\")) {\n        throw new InvalidRequestException(\"groupId is required to delete a membership\");\n    }\n    throw e;\n}","preventionTips":["Resolve groups by name with an explicit not-found error before using the id.","Keep the (userId, groupId) argument order consistent; swap mistakes often yield nulls.","Validate both parameters at the service boundary before invoking the engine.","Never pass optional/nullable group fields straight into deleteMembership."],"tags":["flowable","idm","null-check","membership"],"backgroundTag":"null-argument","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T16:30:33.424Z"}