diff --git a/app/assets/javascripts/work_items/components/work_item_drawer.vue b/app/assets/javascripts/work_items/components/work_item_drawer.vue
index 995395a469ab9e3c1b44b7e55ecaf148ce7c090f..36c0d72c4edd874f7a8f181fb74f8dc4d9715a03 100644
--- a/app/assets/javascripts/work_items/components/work_item_drawer.vue
+++ b/app/assets/javascripts/work_items/components/work_item_drawer.vue
@@ -169,6 +169,8 @@ export default {
     '.pika-single',
     '.atwho-container',
     '.tippy-content .gl-new-dropdown-panel',
+    '#blocked-by-issues-modal',
+    '#open-children-warning-modal',
   ],
 };
 </script>
diff --git a/app/assets/javascripts/work_items/components/work_item_state_toggle.vue b/app/assets/javascripts/work_items/components/work_item_state_toggle.vue
index 4a5afb230dad9810c9127a3073d47fa93188b06a..3b3841ba3faea51d492dceb92a5726cf97e1c82f 100644
--- a/app/assets/javascripts/work_items/components/work_item_state_toggle.vue
+++ b/app/assets/javascripts/work_items/components/work_item_state_toggle.vue
@@ -13,10 +13,11 @@ import {
   LINKED_CATEGORIES_MAP,
   i18n,
 } from '../constants';
-import { findLinkedItemsWidget } from '../utils';
+import { findHierarchyWidgets, findLinkedItemsWidget } from '../utils';
 import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
 import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql';
 import workItemLinkedItemsQuery from '../graphql/work_item_linked_items.query.graphql';
+import workItemOpenChildCountQuery from '../graphql/open_child_count.query.graphql';
 
 export default {
   components: {
@@ -63,6 +64,7 @@ export default {
     return {
       updateInProgress: false,
       blockerItems: [],
+      openChildItemsCount: 0,
     };
   },
   apollo: {
@@ -101,7 +103,7 @@ export default {
       update({ workspace }) {
         if (!workspace?.workItem) return [];
 
-        const linkedWorkItems = findLinkedItemsWidget(workspace.workItem).linkedItems?.nodes || [];
+        const linkedWorkItems = findLinkedItemsWidget(workspace.workItem)?.linkedItems?.nodes || [];
 
         return linkedWorkItems.filter((item) => {
           return item.linkType === LINKED_CATEGORIES_MAP.IS_BLOCKED_BY;
@@ -113,6 +115,32 @@ export default {
         Sentry.captureException(new Error(msg));
       },
     },
+    openChildItemsCount: {
+      query: workItemOpenChildCountQuery,
+      variables() {
+        return {
+          fullPath: this.fullPath,
+          iid: this.workItemIid,
+        };
+      },
+      skip() {
+        return !this.workItemIid;
+      },
+      update({ namespace }) {
+        if (!namespace?.workItem) return 0;
+
+        /** @type {Array<{countsByState: { opened : number }}> } */
+        const countsByType = findHierarchyWidgets(namespace.workItem.widgets)?.rolledUpCountsByType;
+
+        if (!countsByType) {
+          return 0;
+        }
+
+        const total = countsByType.reduce((acc, curr) => acc + curr.countsByState.opened, 0);
+
+        return total;
+      },
+    },
   },
   computed: {
     isWorkItemOpen() {
@@ -146,24 +174,46 @@ export default {
     isBlocked() {
       return this.blockerItems.length > 0;
     },
+    hasOpenChildren() {
+      return this.openChildItemsCount > 0;
+    },
     action() {
-      if (this.isBlocked && this.isWorkItemOpen) {
-        return () => this.$refs.blockedByIssuesModal.show();
+      if (this.isWorkItemOpen) {
+        if (this.isBlocked) {
+          return () => this.$refs.blockedByIssuesModal.show();
+        }
+        if (this.hasOpenChildren) {
+          return () => this.$refs.openChildrenWarningModal.show();
+        }
       }
       return this.updateWorkItem;
     },
-    modalTitle() {
+    blockedByModalTitle() {
       return sprintfWorkItem(
         s__('WorkItem|Are you sure you want to close this blocked %{workItemType}?'),
         this.workItemType,
       );
     },
-    modalBody() {
+    blockedByModalBody() {
       return sprintfWorkItem(
         s__('WorkItem|This %{workItemType} is currently blocked by the following items:'),
         this.workItemType,
       );
     },
+    openChildrenModalTitle() {
+      return sprintfWorkItem(
+        s__('WorkItem|Are you sure you want to close this %{workItemType}?'),
+        this.workItemType,
+      );
+    },
+    openChildrenModalBody() {
+      return sprintfWorkItem(
+        s__(
+          'WorkItem|This %{workItemType} has open child items. If you close this %{workItemType}, they will remain open.',
+        ),
+        this.workItemType,
+      );
+    },
     modalActionCancel() {
       return {
         text: __('Cancel'),
@@ -235,17 +285,30 @@ export default {
     <gl-modal
       ref="blockedByIssuesModal"
       modal-id="blocked-by-issues-modal"
+      data-testid="blocked-by-issues-modal"
       :action-cancel="modalActionCancel"
       :action-primary="modalActionPrimary"
-      :title="modalTitle"
+      :title="blockedByModalTitle"
       @primary="updateWorkItem"
     >
-      <p>{{ modalBody }}</p>
+      <p>{{ blockedByModalBody }}</p>
       <ul>
         <li v-for="issue in blockerItems" :key="issue.workItem.iid">
           <gl-link :href="issue.workItem.webUrl">#{{ issue.workItem.iid }}</gl-link>
         </li>
       </ul>
     </gl-modal>
+
+    <gl-modal
+      ref="openChildrenWarningModal"
+      modal-id="open-children-warning-modal"
+      data-testid="open-children-warning-modal"
+      :action-cancel="modalActionCancel"
+      :action-primary="modalActionPrimary"
+      :title="openChildrenModalTitle"
+      @primary="updateWorkItem"
+    >
+      <p>{{ openChildrenModalBody }}</p>
+    </gl-modal>
   </span>
 </template>
diff --git a/app/assets/javascripts/work_items/graphql/open_child_count.query.graphql b/app/assets/javascripts/work_items/graphql/open_child_count.query.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..2cd6a335938e0d716d39f8462d472bf9913d371a
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/open_child_count.query.graphql
@@ -0,0 +1,25 @@
+query openChildItemCount($fullPath: ID!, $iid: String!) {
+  namespace(fullPath: $fullPath) {
+    id
+    workItem(iid: $iid) {
+      id
+      widgets {
+        ... on WorkItemWidgetHierarchy {
+          type
+          rolledUpCountsByType {
+            countsByState {
+              opened
+              all
+              closed
+            }
+            workItemType {
+              id
+              name
+              iconName
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/app/assets/javascripts/work_items/graphql/work_item_hierarchy.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_hierarchy.fragment.graphql
index 6a3fbc217d050490a06f331bfb14f81e3a2cf7ee..7c8538a3ea0c4106387d60ab72f985f9db3ff8c8 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_hierarchy.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_hierarchy.fragment.graphql
@@ -32,6 +32,7 @@ fragment WorkItemHierarchy on WorkItem {
       }
       rolledUpCountsByType {
         countsByState {
+          opened
           all
           closed
         }
diff --git a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
index fc94c898cf34491bc8bf795024dd3624b74fac71..8e6b90895e3c31f61e5e7c851a169cf8a53fb136 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
@@ -52,6 +52,7 @@ fragment WorkItemWidgets on WorkItemWidget {
     hasParent
     rolledUpCountsByType {
       countsByState {
+        opened
         all
         closed
       }
diff --git a/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql b/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
index cfcc20db9c4033b4736096f00349e20020b05202..3415b5b6f2558c7dcea113e79875377a5ffd1640 100644
--- a/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
+++ b/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
@@ -68,6 +68,7 @@ fragment WorkItemWidgets on WorkItemWidget {
     hasParent
     rolledUpCountsByType {
       countsByState {
+        opened
         all
         closed
       }
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e749fd007c6df2c39992d22709eb99924e9c909c..c04859bdae37897f79db986839fad8e32c1faa0b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -62193,6 +62193,9 @@ msgstr ""
 msgid "WorkItem|Are you sure you want to cancel editing?"
 msgstr ""
 
+msgid "WorkItem|Are you sure you want to close this %{workItemType}?"
+msgstr ""
+
 msgid "WorkItem|Are you sure you want to close this blocked %{workItemType}?"
 msgstr ""
 
@@ -62574,6 +62577,9 @@ msgstr ""
 msgid "WorkItem|The current task"
 msgstr ""
 
+msgid "WorkItem|This %{workItemType} has open child items. If you close this %{workItemType}, they will remain open."
+msgstr ""
+
 msgid "WorkItem|This %{workItemType} is confidential and should only be visible to team members with at least Reporter access"
 msgstr ""
 
diff --git a/spec/frontend/work_items/components/work_item_state_toggle_spec.js b/spec/frontend/work_items/components/work_item_state_toggle_spec.js
index 72130ddd061bdfc100d331970dc60ebac8cf9810..b56360a0ac2f11e0268bfeaadb9384bc9477bcd5 100644
--- a/spec/frontend/work_items/components/work_item_state_toggle_spec.js
+++ b/spec/frontend/work_items/components/work_item_state_toggle_spec.js
@@ -1,10 +1,10 @@
-import { GlButton, GlModal, GlLink } from '@gitlab/ui';
+import { GlButton, GlLink } from '@gitlab/ui';
 import Vue from 'vue';
 import VueApollo from 'vue-apollo';
-import { shallowMount } from '@vue/test-utils';
 import createMockApollo from 'helpers/mock_apollo_helper';
 import { mockTracking } from 'helpers/tracking_helper';
 import waitForPromises from 'helpers/wait_for_promises';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 import WorkItemStateToggle from '~/work_items/components/work_item_state_toggle.vue';
 import {
   STATE_OPEN,
@@ -16,11 +16,15 @@ import {
 import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
 import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
 import workItemLinkedItemsQuery from '~/work_items/graphql/work_item_linked_items.query.graphql';
+import workItemOpenChildCountQuery from '~/work_items/graphql/open_child_count.query.graphql';
 import {
   updateWorkItemMutationResponse,
   mockBlockedByLinkedItem,
   workItemByIidResponseFactory,
   workItemBlockedByLinkedItemsResponse,
+  workItemNoBlockedByLinkedItemsResponse,
+  mockOpenChildrenCount,
+  mockNoOpenChildrenCount,
 } from '../mock_data';
 
 describe('Work Item State toggle button component', () => {
@@ -34,27 +38,32 @@ describe('Work Item State toggle button component', () => {
   const querySuccessHander = jest.fn().mockResolvedValue(workItemQueryResponse);
   const workItemBlockedByItemsSuccessHandler = jest
     .fn()
-    .mockResolvedValue(workItemBlockedByLinkedItemsResponse);
+    .mockResolvedValue(workItemNoBlockedByLinkedItemsResponse);
+  const openChildCountSuccessHandler = jest.fn().mockResolvedValue(mockNoOpenChildrenCount);
 
   const findStateToggleButton = () => wrapper.findComponent(GlButton);
-  const findModal = () => wrapper.findComponent(GlModal);
-  const findModalLinkAt = (index) => findModal().findAllComponents(GlLink).at(index);
+  const findBlockedByModal = () => wrapper.findByTestId('blocked-by-issues-modal');
+  const findBlockedByModalLinkAt = (index) =>
+    findBlockedByModal().findAllComponents(GlLink).at(index);
+  const findOpenChildrenModal = () => wrapper.findByTestId('open-children-warning-modal');
 
   const { id, iid } = workItemQueryResponse.data.workspace.workItem;
 
   const createComponent = ({
     mutationHandler = mutationSuccessHandler,
     workItemLinkedItemsHandler = workItemBlockedByItemsSuccessHandler,
+    workItemOpenChildCountHandler = openChildCountSuccessHandler,
     canUpdate = true,
     workItemState = STATE_OPEN,
     workItemType = 'Task',
     hasComment = false,
   } = {}) => {
-    wrapper = shallowMount(WorkItemStateToggle, {
+    wrapper = shallowMountExtended(WorkItemStateToggle, {
       apolloProvider: createMockApollo([
         [updateWorkItemMutation, mutationHandler],
         [workItemByIidQuery, querySuccessHander],
         [workItemLinkedItemsQuery, workItemLinkedItemsHandler],
+        [workItemOpenChildCountQuery, workItemOpenChildCountHandler],
       ]),
       propsData: {
         workItemId: id,
@@ -173,18 +182,22 @@ describe('Work Item State toggle button component', () => {
     const blockers = mockBlockedByLinkedItem.linkedItems.nodes;
 
     beforeEach(async () => {
-      createComponent();
+      createComponent({
+        workItemLinkedItemsHandler: jest
+          .fn()
+          .mockResolvedValue(workItemBlockedByLinkedItemsResponse),
+      });
       await waitForPromises();
     });
 
     it('has title text', () => {
-      expect(findModal().attributes('title')).toBe(
+      expect(findBlockedByModal().attributes('title')).toBe(
         'Are you sure you want to close this blocked task?',
       );
     });
 
     it('has body text', () => {
-      expect(findModal().text()).toContain(
+      expect(findBlockedByModal().text()).toContain(
         'This task is currently blocked by the following items:',
       );
     });
@@ -195,12 +208,36 @@ describe('Work Item State toggle button component', () => {
       ${'second'} | ${1}
     `('$ordinal blocked-by issue link', ({ index }) => {
       it('has link text', () => {
-        expect(findModalLinkAt(index).text()).toBe(`#${blockers[index].workItem.iid}`);
+        expect(findBlockedByModalLinkAt(index).text()).toBe(`#${blockers[index].workItem.iid}`);
       });
 
       it('has url', () => {
-        expect(findModalLinkAt(index).attributes('href')).toBe(blockers[index].workItem.webUrl);
+        expect(findBlockedByModalLinkAt(index).attributes('href')).toBe(
+          blockers[index].workItem.webUrl,
+        );
+      });
+    });
+  });
+
+  describe('with open child items', () => {
+    beforeEach(async () => {
+      createComponent({
+        workItemOpenChildCountHandler: jest.fn().mockResolvedValue(mockOpenChildrenCount),
+        workItemType: 'Epic',
       });
+      await waitForPromises();
+    });
+
+    it('has title text', () => {
+      expect(findOpenChildrenModal().attributes('title')).toBe(
+        'Are you sure you want to close this epic?',
+      );
+    });
+
+    it('has body text', () => {
+      expect(findOpenChildrenModal().text()).toContain(
+        'This epic has open child items. If you close this epic, they will remain open.',
+      );
     });
   });
 });
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index a43e90ade340c32934237adb47ccc0caf0cb76be..e8eb4e5b821bd19fcb7f09ae17976c151b3f97c1 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -699,6 +699,13 @@ export const mockBlockedByLinkedItem = {
   __typename: 'WorkItemWidgetLinkedItems',
 };
 
+export const mockNoLinkedItems = {
+  type: WIDGET_TYPE_LINKED_ITEMS,
+  linkedItems: {
+    nodes: [],
+  },
+};
+
 export const mockLinkedItems = {
   type: WIDGET_TYPE_LINKED_ITEMS,
   linkedItems: {
@@ -886,6 +893,20 @@ export const workItemSingleLinkedItemResponse = {
   },
 };
 
+export const workItemNoBlockedByLinkedItemsResponse = {
+  data: {
+    workspace: {
+      __typename: 'Namespace',
+      id: 'gid://gitlab/Group/1',
+      workItem: {
+        id: 'gid://gitlab/WorkItem/2',
+        widgets: [mockNoLinkedItems],
+        __typename: 'WorkItem',
+      },
+    },
+  },
+};
+
 export const workItemBlockedByLinkedItemsResponse = {
   data: {
     workspace: {
@@ -2295,6 +2316,7 @@ export const mockDepthLimitReachedByType = [
 export const mockRolledUpCountsByType = [
   {
     countsByState: {
+      opened: 0,
       all: 3,
       closed: 0,
       __typename: 'WorkItemStateCountsType',
@@ -2309,6 +2331,7 @@ export const mockRolledUpCountsByType = [
   },
   {
     countsByState: {
+      opened: 0,
       all: 5,
       closed: 2,
       __typename: 'WorkItemStateCountsType',
@@ -2323,6 +2346,7 @@ export const mockRolledUpCountsByType = [
   },
   {
     countsByState: {
+      opened: 0,
       all: 2,
       closed: 1,
       __typename: 'WorkItemStateCountsType',
@@ -2358,6 +2382,138 @@ export const mockHierarchyWidget = {
   __typename: 'WorkItemWidgetHierarchy',
 };
 
+export const mockOpenChildrenCount = {
+  data: {
+    namespace: {
+      id: 'gid://gitlab/Group/33',
+      workItem: {
+        id: 'gid://gitlab/WorkItem/843',
+        widgets: [
+          {
+            type: 'HIERARCHY',
+            rolledUpCountsByType: [
+              {
+                countsByState: {
+                  opened: 0,
+                  all: 0,
+                  closed: 0,
+                  __typename: 'WorkItemStateCountsType',
+                },
+                workItemType: {
+                  id: 'gid://gitlab/WorkItems::Type/8',
+                  name: 'Epic',
+                  iconName: 'issue-type-epic',
+                  __typename: 'WorkItemType',
+                },
+                __typename: 'WorkItemTypeCountsByState',
+              },
+              {
+                countsByState: {
+                  opened: 1,
+                  all: 1,
+                  closed: 0,
+                  __typename: 'WorkItemStateCountsType',
+                },
+                workItemType: {
+                  id: 'gid://gitlab/WorkItems::Type/1',
+                  name: 'Issue',
+                  iconName: 'issue-type-issue',
+                  __typename: 'WorkItemType',
+                },
+                __typename: 'WorkItemTypeCountsByState',
+              },
+              {
+                countsByState: {
+                  opened: 0,
+                  all: 0,
+                  closed: 0,
+                  __typename: 'WorkItemStateCountsType',
+                },
+                workItemType: {
+                  id: 'gid://gitlab/WorkItems::Type/5',
+                  name: 'Task',
+                  iconName: 'issue-type-task',
+                  __typename: 'WorkItemType',
+                },
+                __typename: 'WorkItemTypeCountsByState',
+              },
+            ],
+            __typename: 'WorkItemWidgetHierarchy',
+          },
+        ],
+        __typename: 'WorkItem',
+      },
+      __typename: 'Namespace',
+    },
+  },
+};
+
+export const mockNoOpenChildrenCount = {
+  data: {
+    namespace: {
+      id: 'gid://gitlab/Group/33',
+      workItem: {
+        id: 'gid://gitlab/WorkItem/843',
+        widgets: [
+          {
+            type: 'HIERARCHY',
+            rolledUpCountsByType: [
+              {
+                countsByState: {
+                  opened: 0,
+                  all: 0,
+                  closed: 0,
+                  __typename: 'WorkItemStateCountsType',
+                },
+                workItemType: {
+                  id: 'gid://gitlab/WorkItems::Type/8',
+                  name: 'Epic',
+                  iconName: 'issue-type-epic',
+                  __typename: 'WorkItemType',
+                },
+                __typename: 'WorkItemTypeCountsByState',
+              },
+              {
+                countsByState: {
+                  opened: 0,
+                  all: 0,
+                  closed: 0,
+                  __typename: 'WorkItemStateCountsType',
+                },
+                workItemType: {
+                  id: 'gid://gitlab/WorkItems::Type/1',
+                  name: 'Issue',
+                  iconName: 'issue-type-issue',
+                  __typename: 'WorkItemType',
+                },
+                __typename: 'WorkItemTypeCountsByState',
+              },
+              {
+                countsByState: {
+                  opened: 0,
+                  all: 0,
+                  closed: 0,
+                  __typename: 'WorkItemStateCountsType',
+                },
+                workItemType: {
+                  id: 'gid://gitlab/WorkItems::Type/5',
+                  name: 'Task',
+                  iconName: 'issue-type-task',
+                  __typename: 'WorkItemType',
+                },
+                __typename: 'WorkItemTypeCountsByState',
+              },
+            ],
+            __typename: 'WorkItemWidgetHierarchy',
+          },
+        ],
+        __typename: 'WorkItem',
+      },
+      __typename: 'Namespace',
+    },
+  },
+};
+
 export const workItemHierarchyTreeResponse = {
   data: {
     workItem: {
diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb
index d8e53e28135d72d1ffa178b1cec08e9ada7ac0a7..8712eefe2c72a16e60860542de874d375d53dd23 100644
--- a/spec/support/shared_examples/features/work_items_shared_examples.rb
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -422,6 +422,8 @@ def notification_button
 
         click_button(class: 'gl-toggle')
 
+        wait_for_requests
+
         expect(page).to have_button(class: 'gl-toggle is-checked')
       end
     end