diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
index b62a2c7bcd1fce842796df620c097a669b23d621..6c615109bb87e0b64bf8f25836d976dcaa9fbe44 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
@@ -9,6 +9,8 @@ import {
   GlLoadingIcon,
   GlIcon,
   GlTooltipDirective,
+  GlPopover,
+  GlButton,
 } from '@gitlab/ui';
 import { kebabCase, snakeCase } from 'lodash';
 import createFlash from '~/flash';
@@ -17,6 +19,7 @@ import { IssuableType } from '~/issues/constants';
 import { timeFor } from '~/lib/utils/datetime_utility';
 import { __ } from '~/locale';
 import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 import {
   dropdowni18nText,
   Tracking,
@@ -47,7 +50,10 @@ export default {
     GlSearchBoxByType,
     GlIcon,
     GlLoadingIcon,
+    GlPopover,
+    GlButton,
   },
+  mixins: [glFeatureFlagMixin()],
   inject: {
     isClassicSidebar: {
       default: false,
@@ -66,6 +72,7 @@ export default {
       },
     },
   },
+
   props: {
     issuableAttribute: {
       type: String,
@@ -111,6 +118,10 @@ export default {
         };
       },
       update(data) {
+        if (this.glFeatures?.epicWidgetEditConfirmation && this.isEpic) {
+          this.hasCurrentAttribute = data?.workspace?.issuable.hasEpic;
+        }
+
         return data?.workspace?.issuable.attribute;
       },
       error(error) {
@@ -179,6 +190,8 @@ export default {
       updating: false,
       selectedTitle: null,
       currentAttribute: null,
+      hasCurrentAttribute: false,
+      editConfirmation: false,
       attributesList: [],
       tracking: {
         event: Tracking.editEvent,
@@ -228,6 +241,15 @@ export default {
         snake: snakeCase(this.issuableAttribute),
       };
     },
+    shouldShowConfirmationPopover() {
+      if (!this.glFeatures?.epicWidgetEditConfirmation) {
+        return false;
+      }
+
+      return this.isEpic && this.currentAttribute === null && this.hasCurrentAttribute
+        ? !this.editConfirmation
+        : false;
+    },
   },
   methods: {
     updateAttribute(attributeId) {
@@ -299,6 +321,17 @@ export default {
     setFocus() {
       this.$refs.search.focusInput();
     },
+    handlePopoverClose() {
+      this.$refs.popover.$emit('close');
+    },
+    handlePopoverConfirm(cb) {
+      this.editConfirmation = true;
+      this.handlePopoverClose();
+      setTimeout(cb, 0);
+    },
+    handleEditConfirmation() {
+      this.$refs.popover.$emit('open');
+    },
   },
 };
 </script>
@@ -308,10 +341,13 @@ export default {
     ref="editable"
     :title="attributeTypeTitle"
     :data-testid="`${formatIssuableAttribute.kebab}-edit`"
+    :button-id="`${formatIssuableAttribute.kebab}-edit`"
     :tracking="tracking"
+    :should-show-confirmation-popover="shouldShowConfirmationPopover"
     :loading="updating || loading"
     @open="handleOpen"
     @close="handleClose"
+    @edit-confirm="handleEditConfirmation"
   >
     <template #collapsed>
       <slot name="value-collapsed" :current-attribute="currentAttribute">
@@ -332,6 +368,10 @@ export default {
         :class="isClassicSidebar ? 'hide-collapsed' : 'gl-mt-3'"
       >
         <span v-if="updating">{{ selectedTitle }}</span>
+        <template v-else-if="!currentAttribute && hasCurrentAttribute">
+          <gl-icon name="warning" class="gl-text-orange-500" />
+          <span class="gl-text-gray-500">{{ i18n.noPermissionToView }}</span>
+        </template>
         <span v-else-if="!currentAttribute" class="gl-text-gray-500">
           {{ $options.i18n.none }}
         </span>
@@ -354,7 +394,40 @@ export default {
         </slot>
       </div>
     </template>
-    <template #default>
+    <template v-if="shouldShowConfirmationPopover" #default="{ toggle }">
+      <gl-popover
+        ref="popover"
+        :target="`${formatIssuableAttribute.kebab}-edit`"
+        placement="bottomleft"
+        boundary="viewport"
+        triggers="click"
+      >
+        <div class="gl-mb-4 gl-font-base">
+          {{ i18n.editConfirmation }}
+        </div>
+        <div class="gl-display-flex gl-align-items-center">
+          <gl-button
+            size="small"
+            variant="confirm"
+            category="primary"
+            data-testid="confirm-edit-cta"
+            @click.prevent="() => handlePopoverConfirm(toggle)"
+            >{{ i18n.editConfirmationCta }}</gl-button
+          >
+          <gl-button
+            class="gl-ml-auto"
+            size="small"
+            name="cancel"
+            variant="default"
+            category="primary"
+            data-testid="confirm-edit-cancel"
+            @click.prevent="handlePopoverClose"
+            >{{ i18n.editConfirmationCancel }}</gl-button
+          >
+        </div>
+      </gl-popover>
+    </template>
+    <template v-else #default>
       <gl-dropdown
         ref="newDropdown"
         lazy
diff --git a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
index 7551b181a58a9d62bc2ef2c1293743085799ee7f..cc88812c7b078f65c5ab1878b94f2f85dc6cab98 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
@@ -14,6 +14,11 @@ export default {
     },
   },
   props: {
+    buttonId: {
+      type: String,
+      required: false,
+      default: '',
+    },
     title: {
       type: String,
       required: false,
@@ -48,6 +53,11 @@ export default {
       required: false,
       default: true,
     },
+    shouldShowConfirmationPopover: {
+      type: Boolean,
+      required: false,
+      default: false,
+    },
   },
   data() {
     return {
@@ -97,6 +107,11 @@ export default {
       window.removeEventListener('keyup', this.collapseOnEscape);
     },
     toggle({ emitEvent = true } = {}) {
+      if (this.shouldShowConfirmationPopover) {
+        this.$emit('edit-confirm');
+        return;
+      }
+
       if (this.edit) {
         this.collapse({ emitEvent });
       } else {
@@ -132,6 +147,7 @@ export default {
       <slot name="collapsed-right"></slot>
       <gl-button
         v-if="canUpdate && !initialLoading && canEdit"
+        :id="buttonId"
         category="tertiary"
         size="small"
         class="gl-text-gray-900! gl-ml-auto hide-collapsed gl-mr-n2 shortcut-sidebar-dropdown-toggle"
@@ -151,7 +167,7 @@ export default {
         <slot name="collapsed">{{ __('None') }}</slot>
       </div>
       <div v-show="edit" data-testid="expanded-content" :class="{ 'gl-mt-3': !isClassicSidebar }">
-        <slot :edit="edit"></slot>
+        <slot :edit="edit" :toggle="toggle"></slot>
       </div>
     </template>
   </div>
diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js
index 989dc574bc3f58bfc788611ef04e6d22f21aeafa..e6328ad1cbb27af39c4bd44a79afdb312f77d352 100644
--- a/app/assets/javascripts/sidebar/constants.js
+++ b/app/assets/javascripts/sidebar/constants.js
@@ -313,6 +313,22 @@ export function dropdowni18nText(issuableAttribute, issuableType) {
       ),
       { issuableAttribute, issuableType },
     ),
+    noPermissionToView: sprintf(
+      s__("DropdownWidget|You don't have permission to view this %{issuableAttribute}."),
+      { issuableAttribute },
+    ),
+    editConfirmation: sprintf(
+      s__(
+        'DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it.',
+      ),
+      {
+        issuableAttribute,
+      },
+    ),
+    editConfirmationCta: sprintf(s__('DropdownWidget|Edit %{issuableAttribute}'), {
+      issuableAttribute,
+    }),
+    editConfirmationCancel: s__('DropdownWidget|Cancel'),
   };
 }
 
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index d19db2b11abb90c79d09621986c36c0d8ce1b101..32a83f2583ca47720e6f27327d68d51fb86f8697 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -53,6 +53,7 @@ class Projects::IssuesController < Projects::ApplicationController
     push_frontend_feature_flag(:realtime_labels, project)
     push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
     push_frontend_feature_flag(:work_items_hierarchy, project)
+    push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
     push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?)
   end
 
diff --git a/config/feature_flags/development/epic_widget_edit_confirmation.yml b/config/feature_flags/development/epic_widget_edit_confirmation.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6c92ef44e2feabf8c0c625f8b9ff252f7d66d6f8
--- /dev/null
+++ b/config/feature_flags/development/epic_widget_edit_confirmation.yml
@@ -0,0 +1,8 @@
+---
+name: epic_widget_edit_confirmation
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96872
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372429
+milestone: '15.4'
+type: development
+group: group::product planning
+default_enabled: false
diff --git a/ee/app/assets/javascripts/sidebar/queries/project_issue_epic.query.graphql b/ee/app/assets/javascripts/sidebar/queries/project_issue_epic.query.graphql
index 24e92ac34e8b6312dfe2f92af960c21072ed5535..7c9bcdf910791aca6c2472941fb428c0a9d1344e 100644
--- a/ee/app/assets/javascripts/sidebar/queries/project_issue_epic.query.graphql
+++ b/ee/app/assets/javascripts/sidebar/queries/project_issue_epic.query.graphql
@@ -5,6 +5,7 @@ query projectIssueEpic($fullPath: ID!, $iid: String!) {
     id
     issuable: issue(iid: $iid) {
       id
+      hasEpic
       attribute: epic {
         ...EpicFragment
       }
diff --git a/ee/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js b/ee/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
index 1ea5e303acfc3362d12da0899888f43ef3cf02c6..f7ed32421f7974616cdd49b57fc5ad03ad0e5d62 100644
--- a/ee/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
+++ b/ee/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
@@ -22,6 +22,7 @@ import {
   mockEpicMutationResponse,
   mockEpic2,
   emptyGroupEpicsResponse,
+  mockNoPermissionEpicResponse,
 } from '../mock_data';
 
 jest.mock('~/flash');
@@ -32,10 +33,21 @@ describe('SidebarDropdownWidget', () => {
 
   const findDropdown = () => wrapper.findComponent(GlDropdown);
   const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
+  const findPopoverCta = () => wrapper.findByTestId('confirm-edit-cta');
+  const findPopoverCancel = () => wrapper.findByTestId('confirm-edit-cancel');
   const findDropdownItemWithText = (text) =>
     findAllDropdownItems().wrappers.find((x) => x.text() === text);
   const findSelectedAttribute = () => wrapper.findByTestId('select-epic');
 
+  const waitForDropdown = async () => {
+    /** This sequence is important to wait for
+     * dropdown to render
+     */
+    await waitForPromises();
+    jest.runOnlyPendingTimers();
+    await waitForPromises();
+  };
+
   const createComponentWithApollo = async ({
     requestHandlers = [],
     groupEpicsSpy = jest.fn().mockResolvedValue(mockGroupEpicsResponse),
@@ -50,7 +62,11 @@ describe('SidebarDropdownWidget', () => {
 
     wrapper = extendedWrapper(
       mount(SidebarDropdownWidget, {
-        provide: { canUpdate: true, issuableAttributesQueries },
+        provide: {
+          canUpdate: true,
+          issuableAttributesQueries,
+          glFeatures: { epicWidgetEditConfirmation: true },
+        },
         apolloProvider: mockApollo,
         propsData: {
           workspacePath: mockIssue.projectPath,
@@ -223,6 +239,52 @@ describe('SidebarDropdownWidget', () => {
           });
         });
       });
+
+      describe("when attribute type is 'epic'", () => {
+        describe("when user doesn't have permission", () => {
+          it('opens popover on edit click', async () => {
+            await createComponentWithApollo({
+              currentEpicSpy: jest.fn().mockResolvedValue(mockNoPermissionEpicResponse),
+            });
+
+            const spy = jest.spyOn(wrapper.vm.$children[0].$refs.popover, '$emit');
+
+            await clickEdit(wrapper);
+
+            expect(spy).toHaveBeenCalledWith('open');
+
+            spy.mockRestore();
+          });
+
+          it('renders dropdown when popover is confirmed', async () => {
+            await createComponentWithApollo({
+              currentEpicSpy: jest.fn().mockResolvedValue(mockNoPermissionEpicResponse),
+            });
+
+            await clickEdit(wrapper);
+
+            const button = findPopoverCta();
+            button.trigger('click');
+            await waitForDropdown();
+
+            expect(findDropdown().isVisible()).toBe(true);
+          });
+
+          it('does not render dropdown when popover is canceled', async () => {
+            await createComponentWithApollo({
+              currentEpicSpy: jest.fn().mockResolvedValue(mockNoPermissionEpicResponse),
+            });
+
+            await clickEdit(wrapper);
+
+            const button = findPopoverCancel();
+            button.trigger('click');
+            await waitForDropdown();
+
+            expect(findDropdown().exists()).toBe(false);
+          });
+        });
+      });
     });
   });
 });
diff --git a/ee/spec/frontend/sidebar/mock_data.js b/ee/spec/frontend/sidebar/mock_data.js
index eab8cf174648ff7250a2eb39e59d4f807265dca2..d38e8254e8dfed516c0e6d836eb749d89f1b2678 100644
--- a/ee/spec/frontend/sidebar/mock_data.js
+++ b/ee/spec/frontend/sidebar/mock_data.js
@@ -130,7 +130,17 @@ export const noCurrentEpicResponse = {
   data: {
     workspace: {
       id: '1',
-      issuable: { id: mockIssueId, attribute: null, __typename: 'Issue' },
+      issuable: { id: mockIssueId, hasEpic: false, attribute: null, __typename: 'Issue' },
+      __typename: 'Project',
+    },
+  },
+};
+
+export const mockNoPermissionEpicResponse = {
+  data: {
+    workspace: {
+      id: '1',
+      issuable: { id: mockIssueId, hasEpic: true, attribute: null, __typename: 'Issue' },
       __typename: 'Project',
     },
   },
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5b7a47191e9840e449810129f1744bb92ff64d34..26d3839ff81ca3611a8ec6668bc71a94450d6a0e 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -14031,6 +14031,12 @@ msgstr ""
 msgid "DropdownWidget|Assign %{issuableAttribute}"
 msgstr ""
 
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
 msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
 msgstr ""
 
@@ -14046,6 +14052,12 @@ msgstr ""
 msgid "DropdownWidget|No open %{issuableAttribute} found"
 msgstr ""
 
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
 msgid "Due Date"
 msgstr ""
 
diff --git a/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js b/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
index 8ebd2dabfc2e6bb63b21a8029854a361c4dfc500..6761731c0936af253fa1c0e7a761c68ec8f01eb5 100644
--- a/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
+++ b/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
@@ -238,6 +238,24 @@ describe('SidebarDropdownWidget', () => {
         expect(findSelectedAttribute().text()).toBe('None');
       });
     });
+
+    describe("when user doesn't have permission to view current attribute", () => {
+      it('renders no permission text', () => {
+        createComponent({
+          data: {
+            hasCurrentAttribute: true,
+            currentAttribute: null,
+          },
+          queries: {
+            currentAttribute: { loading: false },
+          },
+        });
+
+        expect(findSelectedAttribute().text()).toBe(
+          `You don't have permission to view this ${wrapper.props('issuableAttribute')}.`,
+        );
+      });
+    });
   });
 
   describe('when a user can edit', () => {