From 00c7a1e8db7246e6bf4a6a244937e507aa91c55d Mon Sep 17 00:00:00 2001
From: mateusz-winiarczyk <mateusz.winiarczyk@appunite.com>
Date: Mon, 11 Mar 2024 13:07:35 +0100
Subject: [PATCH] feat(toast): add toast and error handling (MIN-283)

---
 package-lock.json                             |  16 ++
 package.json                                  |   1 +
 .../AppWrapper/AppWrapper.component.tsx       |  14 +-
 .../EditOverlayModal.component.test.tsx       |  72 ++++++
 .../EditOverlayModal/hooks/useEditOverlay.ts  |   4 +-
 .../AvailablePluginsDrawer.constants.ts       |   1 +
 .../LoadPlugin/hooks/useLoadPlugin.test.ts    |  29 +++
 .../LoadPlugin/hooks/useLoadPlugin.ts         |  33 ++-
 .../LoadPluginFromUrl.component.test.tsx      |  32 +++
 .../hooks/useLoadPluginFromUrl.ts             |   9 +
 .../UserOverlayForm.component.test.tsx        |  60 +++++
 .../mapSingleClick/handleAliasResults.ts      |   2 -
 .../mapSingleClick/handleReactionResults.ts   |   2 -
 .../backgrounds/backgrounds.constants.ts      |   1 +
 .../backgrounds/backgrounds.reducers.test.ts  |   7 +-
 src/redux/backgrounds/backgrounds.thunks.ts   |  23 +-
 src/redux/bioEntity/bioEntity.constants.ts    |   3 +
 .../bioEntity/bioEntity.reducers.test.ts      |   5 +-
 src/redux/bioEntity/bioEntity.thunks.test.ts  |  20 ++
 src/redux/bioEntity/bioEntity.thunks.ts       |  69 ++++--
 src/redux/chemicals/chemicals.constants.ts    |   2 +
 .../chemicals/chemicals.reducers.test.ts      |   5 +-
 src/redux/chemicals/chemicals.thunks.test.ts  |  10 +
 src/redux/chemicals/chemicals.thunks.ts       |  47 ++--
 .../comparmentPathways.constants.ts           |   2 +
 .../compartmentPathways.reducers.test.ts      |   5 +-
 .../compartmentPathways.thunks.ts             |  23 +-
 .../configuration/configuration.constants.ts  |   4 +
 .../configuration/configuration.thunks.ts     |  40 +++-
 src/redux/drawer/drawer.constants.ts          |   4 +
 src/redux/drawer/drawer.thunks.ts             |  53 +++--
 src/redux/drugs/drugs.constants.ts            |   2 +
 src/redux/drugs/drugs.reducers.test.ts        |   5 +-
 src/redux/drugs/drugs.thunks.ts               |  41 ++--
 src/redux/export/export.constants.ts          |   2 +
 src/redux/export/export.reducers.test.ts      |  12 +-
 src/redux/export/export.thunks.ts             |  52 +++--
 src/redux/map/map.constants.ts                |   8 +
 src/redux/map/map.thunks.ts                   |  70 ++++--
 .../middlewares/error.middleware.test.ts      |  87 +++++++
 src/redux/middlewares/error.middleware.ts     |  34 +++
 src/redux/models/models.constants.ts          |   1 +
 src/redux/models/models.reducers.test.ts      |   5 +-
 src/redux/models/models.thunks.ts             |  19 +-
 .../overlayBioEntity.constants.ts             |   4 +
 .../overlayBioEntity.thunk.ts                 |  74 ++++--
 src/redux/overlays/overlays.constants.ts      |   9 +
 src/redux/overlays/overlays.reducers.test.ts  |   5 +-
 src/redux/overlays/overlays.thunks.ts         | 213 +++++++++++-------
 src/redux/plugins/plugins.constants.ts        |   4 +
 src/redux/plugins/plugins.reducers.test.ts    |   4 +-
 src/redux/plugins/plugins.thunks.ts           | 117 ++++++----
 src/redux/project/project.constants.ts        |   1 +
 src/redux/project/project.reducers.test.ts    |   5 +-
 src/redux/project/project.thunks.ts           |  18 +-
 .../publications/publications.constatns.ts    |   1 +
 src/redux/publications/publications.thunks.ts |  19 +-
 src/redux/reactions/reactions.constants.ts    |   2 +
 src/redux/reactions/reactions.thunks.ts       |  24 +-
 src/redux/search/search.constants.ts          |   2 +
 src/redux/search/search.thunks.ts             |  30 ++-
 src/redux/statistics/statistics.constants.ts  |   1 +
 .../statistics/statistics.reducers.test.ts    |   5 +-
 src/redux/statistics/statistics.thunks.ts     |  18 +-
 src/redux/store.ts                            |   3 +-
 src/redux/user/user.thunks.ts                 |  26 ++-
 .../map/triggerSearch/searchByQuery.ts        |   3 -
 src/shared/Toast/Toast.component.test.tsx     |  29 +++
 src/shared/Toast/Toast.component.tsx          |  30 +++
 src/shared/Toast/index.ts                     |   1 +
 src/types/store.ts                            |   3 +
 .../getErrorMessage.constants.ts              |  11 +
 .../getErrorMessage/getErrorMessage.test.ts   |  34 +++
 .../getErrorMessage.test.utils.ts             |  15 ++
 src/utils/getErrorMessage/getErrorMessage.ts  |  29 +++
 .../getErrorMessage/getErrorMessage.types.ts  |   3 +
 .../getErrorMessage.utils.test.ts             |  22 ++
 .../getErrorMessage/getErrorMessage.utils.ts  |  14 ++
 src/utils/getErrorMessage/index.ts            |   1 +
 src/utils/showToast.test.tsx                  |  21 ++
 src/utils/showToast.tsx                       |  13 ++
 81 files changed, 1395 insertions(+), 355 deletions(-)
 create mode 100644 src/components/Map/Drawer/AvailablePluginsDrawer/AvailablePluginsDrawer.constants.ts
 create mode 100644 src/redux/backgrounds/backgrounds.constants.ts
 create mode 100644 src/redux/chemicals/chemicals.constants.ts
 create mode 100644 src/redux/drugs/drugs.constants.ts
 create mode 100644 src/redux/export/export.constants.ts
 create mode 100644 src/redux/middlewares/error.middleware.test.ts
 create mode 100644 src/redux/middlewares/error.middleware.ts
 create mode 100644 src/redux/models/models.constants.ts
 create mode 100644 src/redux/overlayBioEntity/overlayBioEntity.constants.ts
 create mode 100644 src/redux/project/project.constants.ts
 create mode 100644 src/redux/publications/publications.constatns.ts
 create mode 100644 src/redux/statistics/statistics.constants.ts
 create mode 100644 src/shared/Toast/Toast.component.test.tsx
 create mode 100644 src/shared/Toast/Toast.component.tsx
 create mode 100644 src/shared/Toast/index.ts
 create mode 100644 src/types/store.ts
 create mode 100644 src/utils/getErrorMessage/getErrorMessage.constants.ts
 create mode 100644 src/utils/getErrorMessage/getErrorMessage.test.ts
 create mode 100644 src/utils/getErrorMessage/getErrorMessage.test.utils.ts
 create mode 100644 src/utils/getErrorMessage/getErrorMessage.ts
 create mode 100644 src/utils/getErrorMessage/getErrorMessage.types.ts
 create mode 100644 src/utils/getErrorMessage/getErrorMessage.utils.test.ts
 create mode 100644 src/utils/getErrorMessage/getErrorMessage.utils.ts
 create mode 100644 src/utils/getErrorMessage/index.ts
 create mode 100644 src/utils/showToast.test.tsx
 create mode 100644 src/utils/showToast.tsx

diff --git a/package-lock.json b/package-lock.json
index a1966db0..18eaa14c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,6 +35,7 @@
         "react-dom": "18.2.0",
         "react-dropzone": "^14.2.3",
         "react-redux": "^8.1.2",
+        "sonner": "^1.4.3",
         "tailwind-merge": "^1.14.0",
         "tailwindcss": "3.3.3",
         "ts-deepmerge": "^6.2.0",
@@ -12410,6 +12411,15 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
+    "node_modules/sonner": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.3.tgz",
+      "integrity": "sha512-SArYlHbkjqRuLiR0iGY2ZSr09oOrxw081ZZkQPfXrs8aZQLIBOLOdzTYxGJB5yIZ7qL56UEPmrX1YqbODwG0Lw==",
+      "peerDependencies": {
+        "react": "^18.0.0",
+        "react-dom": "^18.0.0"
+      }
+    },
     "node_modules/source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -22963,6 +22973,12 @@
         }
       }
     },
+    "sonner": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.4.3.tgz",
+      "integrity": "sha512-SArYlHbkjqRuLiR0iGY2ZSr09oOrxw081ZZkQPfXrs8aZQLIBOLOdzTYxGJB5yIZ7qL56UEPmrX1YqbODwG0Lw==",
+      "requires": {}
+    },
     "source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
diff --git a/package.json b/package.json
index f44f22ef..54dfdef6 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
     "react-dom": "18.2.0",
     "react-dropzone": "^14.2.3",
     "react-redux": "^8.1.2",
+    "sonner": "^1.4.3",
     "tailwind-merge": "^1.14.0",
     "tailwindcss": "3.3.3",
     "ts-deepmerge": "^6.2.0",
diff --git a/src/components/AppWrapper/AppWrapper.component.tsx b/src/components/AppWrapper/AppWrapper.component.tsx
index 3b59e82b..5014ee69 100644
--- a/src/components/AppWrapper/AppWrapper.component.tsx
+++ b/src/components/AppWrapper/AppWrapper.component.tsx
@@ -2,6 +2,7 @@ import { store } from '@/redux/store';
 import { MapInstanceProvider } from '@/utils/context/mapInstanceContext';
 import { ReactNode } from 'react';
 import { Provider } from 'react-redux';
+import { Toaster } from 'sonner';
 
 interface AppWrapperProps {
   children: ReactNode;
@@ -9,6 +10,17 @@ interface AppWrapperProps {
 
 export const AppWrapper = ({ children }: AppWrapperProps): JSX.Element => (
   <MapInstanceProvider>
-    <Provider store={store}>{children}</Provider>
+    <Provider store={store}>
+      <>
+        <Toaster
+          position="top-center"
+          visibleToasts={1}
+          style={{
+            width: '700px',
+          }}
+        />
+        {children}
+      </>
+    </Provider>
   </MapInstanceProvider>
 );
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
index f0934d79..990e4fbd 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/EditOverlayModal.component.test.tsx
@@ -11,10 +11,13 @@ import { HttpStatusCode } from 'axios';
 import { DEFAULT_ERROR } from '@/constants/errors';
 import { act } from 'react-dom/test-utils';
 import { OVERLAYS_INITIAL_STATE_MOCK } from '@/redux/overlays/overlays.mock';
+import { showToast } from '@/utils/showToast';
 import { Modal } from '../Modal.component';
 
 const mockedAxiosClient = mockNetworkResponse();
 
+jest.mock('../../../../utils/showToast');
+
 const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
   const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
 
@@ -31,6 +34,9 @@ const renderComponent = (initialStoreState: InitialStoreState = {}): { store: St
 };
 
 describe('EditOverlayModal - component', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
   it('should render modal with correct data', () => {
     renderComponent({
       modal: {
@@ -101,6 +107,39 @@ describe('EditOverlayModal - component', () => {
     expect(loading).toBe('succeeded');
     expect(removeButton).not.toBeVisible();
   });
+  it('should show toast after successful removing user overlay', async () => {
+    renderComponent({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay',
+        editOverlayState: overlayFixture,
+        molArtState: {},
+        overviewImagesState: {},
+      },
+      overlays: OVERLAYS_INITIAL_STATE_MOCK,
+    });
+    mockedAxiosClient
+      .onDelete(apiPath.removeOverlay(overlayFixture.idObject))
+      .reply(HttpStatusCode.Ok, {});
+
+    const removeButton = screen.getByTestId('remove-button');
+    expect(removeButton).toBeVisible();
+    await act(() => {
+      removeButton.click();
+    });
+
+    expect(showToast).toHaveBeenCalledWith({
+      message: 'User overlay removed successfully',
+      type: 'success',
+    });
+  });
   it('should handle save edited user overlay', async () => {
     const { store } = renderComponent({
       user: {
@@ -133,6 +172,39 @@ describe('EditOverlayModal - component', () => {
     expect(loading).toBe('succeeded');
     expect(saveButton).not.toBeVisible();
   });
+  it('should show toast after successful editing user overlay', async () => {
+    renderComponent({
+      user: {
+        authenticated: true,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+        login: 'test',
+      },
+      modal: {
+        isOpen: true,
+        modalTitle: overlayFixture.name,
+        modalName: 'edit-overlay',
+        editOverlayState: overlayFixture,
+        molArtState: {},
+        overviewImagesState: {},
+      },
+      overlays: OVERLAYS_INITIAL_STATE_MOCK,
+    });
+    mockedAxiosClient
+      .onPatch(apiPath.updateOverlay(overlayFixture.idObject))
+      .reply(HttpStatusCode.Ok, overlayFixture);
+
+    const saveButton = screen.getByTestId('save-button');
+    expect(saveButton).toBeVisible();
+    await act(() => {
+      saveButton.click();
+    });
+
+    expect(showToast).toHaveBeenCalledWith({
+      message: 'User overlay updated successfully',
+      type: 'success',
+    });
+  });
 
   it('should handle cancel edit user overlay', async () => {
     const { store } = renderComponent({
diff --git a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
index 63a98d5e..a8d02776 100644
--- a/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
+++ b/src/components/FunctionalArea/Modal/EditOverlayModal/hooks/useEditOverlay.ts
@@ -76,10 +76,10 @@ export const useEditOverlay = (): UseEditOverlayReturn => {
   };
 
   const handleSaveEditedOverlay = async (): Promise<void> => {
-    if (!currentEditedOverlay || !name || !description || !login) return;
+    if (!currentEditedOverlay || !name || !login) return;
     await handleUpdateOverlay({
       editedOverlay: currentEditedOverlay,
-      overlayDescription: description,
+      overlayDescription: description || '',
       overlayName: name,
     });
 
diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/AvailablePluginsDrawer.constants.ts b/src/components/Map/Drawer/AvailablePluginsDrawer/AvailablePluginsDrawer.constants.ts
new file mode 100644
index 00000000..e628e141
--- /dev/null
+++ b/src/components/Map/Drawer/AvailablePluginsDrawer/AvailablePluginsDrawer.constants.ts
@@ -0,0 +1 @@
+export const PLUGIN_LOADING_ERROR_PREFIX = 'Failed to load plugin';
diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.test.ts b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.test.ts
index 0ea19da0..f54c8f5b 100644
--- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.test.ts
+++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.test.ts
@@ -7,10 +7,12 @@ import { renderHook, waitFor } from '@testing-library/react';
 import axios, { HttpStatusCode } from 'axios';
 import MockAdapter from 'axios-mock-adapter';
 import { act } from 'react-dom/test-utils';
+import { showToast } from '@/utils/showToast';
 import { useLoadPlugin } from './useLoadPlugin';
 
 const mockedAxiosClient = new MockAdapter(axios);
 jest.mock('../../../../../../services/pluginsManager/pluginsManager');
+jest.mock('../../../../../../utils/showToast');
 
 describe('useLoadPlugin', () => {
   afterEach(() => {
@@ -86,4 +88,31 @@ describe('useLoadPlugin', () => {
       });
     });
   });
+  it('should show toast if plugin failed to load', async () => {
+    const hash = 'pluginHash';
+    const pluginUrl = 'http://example.com/plugin.js';
+
+    const { Wrapper } = getReduxStoreWithActionsListener(INITIAL_STORE_STATE_MOCK);
+
+    mockedAxiosClient.onGet(pluginUrl).reply(HttpStatusCode.Forbidden, null);
+
+    const {
+      result: {
+        current: { togglePlugin },
+      },
+    } = renderHook(() => useLoadPlugin({ hash, pluginUrl }), {
+      wrapper: Wrapper,
+    });
+
+    togglePlugin();
+
+    await waitFor(() => {
+      expect(showToast).toHaveBeenCalled();
+      expect(showToast).toHaveBeenCalledWith({
+        message:
+          "Failed to load plugin: Access Forbidden! You don't have permission to access this resource.",
+        type: 'error',
+      });
+    });
+  });
 });
diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.ts b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.ts
index 45434730..ebce11ec 100644
--- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.ts
+++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPlugin/hooks/useLoadPlugin.ts
@@ -7,7 +7,10 @@ import {
 } from '@/redux/plugins/plugins.selectors';
 import { removePlugin } from '@/redux/plugins/plugins.slice';
 import { PluginsManager } from '@/services/pluginsManager';
+import { showToast } from '@/utils/showToast';
 import axios from 'axios';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { PLUGIN_LOADING_ERROR_PREFIX } from '../../AvailablePluginsDrawer.constants';
 
 type UseLoadPluginReturnType = {
   togglePlugin: () => void;
@@ -37,21 +40,29 @@ export const useLoadPlugin = ({
   const dispatch = useAppDispatch();
 
   const handleLoadPlugin = async (): Promise<void> => {
-    const response = await axios(pluginUrl);
-    const pluginScript = response.data;
+    try {
+      const response = await axios(pluginUrl);
+      const pluginScript = response.data;
 
-    /* eslint-disable no-new-func */
-    const loadPlugin = new Function(pluginScript);
+      /* eslint-disable no-new-func */
+      const loadPlugin = new Function(pluginScript);
 
-    PluginsManager.setHashedPlugin({
-      pluginUrl,
-      pluginScript,
-    });
+      PluginsManager.setHashedPlugin({
+        pluginUrl,
+        pluginScript,
+      });
 
-    loadPlugin();
+      loadPlugin();
 
-    if (onPluginLoaded) {
-      onPluginLoaded();
+      if (onPluginLoaded) {
+        onPluginLoaded();
+      }
+    } catch (error) {
+      const errorMessage = getErrorMessage({
+        error,
+        prefix: PLUGIN_LOADING_ERROR_PREFIX,
+      });
+      showToast({ type: 'error', message: errorMessage });
     }
   };
 
diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx
index e6a8094d..1b347d6e 100644
--- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx
+++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/LoadPluginFromUrl.component.test.tsx
@@ -13,11 +13,14 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
 import axios, { HttpStatusCode } from 'axios';
 import MockAdapter from 'axios-mock-adapter';
 import { act } from 'react-dom/test-utils';
+import { showToast } from '@/utils/showToast';
 import { LoadPluginFromUrl } from './LoadPluginFromUrl.component';
 
 const mockedAxiosApiClient = mockNetworkResponse();
 const mockedAxiosClient = new MockAdapter(axios);
 
+jest.mock('../../../../../utils/showToast');
+
 const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => {
   const { Wrapper, store } = getReduxWrapperWithStore(initialStore);
   return (
@@ -163,6 +166,35 @@ describe('LoadPluginFromUrl - component', () => {
       const button = screen.getByTestId('load-plugin-button');
       expect(button).toBeDisabled();
     });
+    it('should show toast if plugin failed to load', async () => {
+      const pluginUrl = 'http://example.com/plugin.js';
+      mockedAxiosClient.onGet(pluginUrl).reply(HttpStatusCode.Unauthorized, null);
+
+      global.URL.canParse = jest.fn().mockReturnValue(true);
+
+      renderComponent();
+      const input = screen.getByTestId('load-plugin-input-url');
+      expect(input).toBeVisible();
+
+      act(() => {
+        fireEvent.change(input, { target: { value: pluginUrl } });
+      });
+
+      const button = screen.getByTestId('load-plugin-button');
+
+      act(() => {
+        button.click();
+      });
+
+      await waitFor(() => {
+        expect(showToast).toHaveBeenCalled();
+        expect(showToast).toHaveBeenCalledWith({
+          message:
+            "Failed to load plugin: You're not authorized to access this resource. Please log in or check your credentials.",
+          type: 'error',
+        });
+      });
+    });
     it('should set plugin active tab in drawer as loaded plugin', async () => {
       const pluginUrl = 'http://example.com/plugin.js';
       const pluginScript = `function init() {} init()`;
diff --git a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts
index 93d0f014..f95bdf7e 100644
--- a/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts
+++ b/src/components/Map/Drawer/AvailablePluginsDrawer/LoadPluginFromUrl/hooks/useLoadPluginFromUrl.ts
@@ -3,8 +3,11 @@ import { useAppSelector } from '@/redux/hooks/useAppSelector';
 import { activePluginsDataSelector } from '@/redux/plugins/plugins.selectors';
 import { setCurrentDrawerPluginHash } from '@/redux/plugins/plugins.slice';
 import { PluginsManager } from '@/services/pluginsManager';
+import { showToast } from '@/utils/showToast';
 import axios from 'axios';
 import { ChangeEvent, useMemo, useState } from 'react';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { PLUGIN_LOADING_ERROR_PREFIX } from '../../AvailablePluginsDrawer.constants';
 
 type UseLoadPluginReturnType = {
   handleChangePluginUrl: (event: ChangeEvent<HTMLInputElement>) => void;
@@ -49,6 +52,12 @@ export const useLoadPluginFromUrl = (): UseLoadPluginReturnType => {
       setPluginUrl('');
 
       handleSetCurrentDrawerPluginHash(hash);
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: PLUGIN_LOADING_ERROR_PREFIX });
+      showToast({
+        type: 'error',
+        message: errorMessage,
+      });
     } finally {
       setIsLoading(false);
     }
diff --git a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
index 474934d8..b9966147 100644
--- a/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
+++ b/src/components/Map/Drawer/OverlaysDrawer/UserOverlayForm/UserOverlayForm.component.test.tsx
@@ -21,8 +21,11 @@ import {
 } from '@/models/fixtures/overlaysFixture';
 import { OVERLAYS_INITIAL_STATE_MOCK } from '@/redux/overlays/overlays.mock';
 import { DEFAULT_ERROR } from '@/constants/errors';
+import { showToast } from '@/utils/showToast';
 import { UserOverlayForm } from './UserOverlayForm.component';
 
+jest.mock('../../../../../utils/showToast');
+
 const mockedAxiosClient = mockNetworkResponse();
 
 const renderComponent = (initialStore?: InitialStoreState): { store: StoreType } => {
@@ -57,6 +60,9 @@ const renderComponentWithActionListener = (
 };
 
 describe('UserOverlayForm - Component', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
   it('renders the UserOverlayForm component', () => {
     renderComponent();
 
@@ -249,4 +255,58 @@ describe('UserOverlayForm - Component', () => {
       expect(refetchedUserOverlays).toEqual(overlaysFixture);
     });
   });
+  it('should show toast after successful creating user overlays', async () => {
+    mockedAxiosClient
+      .onPost(apiPath.createOverlayFile())
+      .reply(HttpStatusCode.Ok, createdOverlayFileFixture);
+
+    mockedAxiosClient
+      .onPost(apiPath.uploadOverlayFileContent(createdOverlayFileFixture.id))
+      .reply(HttpStatusCode.Ok, uploadedOverlayFileContentFixture);
+
+    mockedAxiosClient
+      .onPost(apiPath.createOverlay(projectFixture.projectId))
+      .reply(HttpStatusCode.Ok, createdOverlayFixture);
+
+    mockedAxiosClient
+      .onGet(apiPath.getAllUserOverlaysByCreatorQuery({ creator: 'test', publicOverlay: false }))
+      .reply(HttpStatusCode.Ok, overlaysFixture);
+
+    const { store } = renderComponent({
+      user: {
+        authenticated: true,
+        error: DEFAULT_ERROR,
+        login: 'test',
+        loading: 'succeeded',
+      },
+      project: {
+        data: projectFixture,
+        loading: 'succeeded',
+        error: DEFAULT_ERROR,
+      },
+      overlays: OVERLAYS_INITIAL_STATE_MOCK,
+    });
+
+    const userOverlays = store.getState().overlays.userOverlays.data;
+
+    expect(userOverlays).toEqual([]);
+
+    fireEvent.change(screen.getByTestId('overlay-name'), { target: { value: 'Test Overlay' } });
+    fireEvent.change(screen.getByTestId('overlay-description'), {
+      target: { value: 'Description Overlay' },
+    });
+    fireEvent.change(screen.getByTestId('overlay-elements-list'), {
+      target: { value: 'Elements List Overlay' },
+    });
+
+    fireEvent.click(screen.getByLabelText('upload overlay'));
+
+    await waitFor(() => {
+      expect(showToast).toHaveBeenCalled();
+      expect(showToast).toHaveBeenCalledWith({
+        message: 'User overlay added successfully',
+        type: 'success',
+      });
+    });
+  });
 });
diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts
index 40874a2d..bab0bd62 100644
--- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts
+++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts
@@ -29,7 +29,5 @@ export const handleAliasResults =
           if (hasFitBounds) {
             searchFitBounds(fitBoundsZoom);
           }
-        }).catch(() => {
-          // TODO to discuss manage state of failure
         });
     };
diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts
index 24332182..bcadc4a1 100644
--- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts
+++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleReactionResults.ts
@@ -41,7 +41,5 @@ export const handleReactionResults =
         if (hasFitBounds) {
           searchFitBounds(fitBoundsZoom);
         }
-      }).catch(() => {
-        // TODO to discuss manage state of failure
       });
     };
diff --git a/src/redux/backgrounds/backgrounds.constants.ts b/src/redux/backgrounds/backgrounds.constants.ts
new file mode 100644
index 00000000..0fa103f2
--- /dev/null
+++ b/src/redux/backgrounds/backgrounds.constants.ts
@@ -0,0 +1 @@
+export const BACKGROUNDS_FETCHING_ERROR_PREFIX = 'Failed to fetch backgrounds';
diff --git a/src/redux/backgrounds/backgrounds.reducers.test.ts b/src/redux/backgrounds/backgrounds.reducers.test.ts
index 9b99161a..4f70938f 100644
--- a/src/redux/backgrounds/backgrounds.reducers.test.ts
+++ b/src/redux/backgrounds/backgrounds.reducers.test.ts
@@ -11,6 +11,8 @@ import backgroundsReducer from './backgrounds.slice';
 import { getAllBackgroundsByProjectId } from './backgrounds.thunks';
 import { BackgroundsState } from './backgrounds.types';
 
+jest.mock('../../utils/showToast');
+
 const mockedAxiosClient = mockNetworkResponse();
 
 const INITIAL_STATE: BackgroundsState = {
@@ -49,13 +51,16 @@ describe('backgrounds reducer', () => {
       .onGet(apiPath.getAllBackgroundsByProjectIdQuery(PROJECT_ID))
       .reply(HttpStatusCode.NotFound, []);
 
-    const { type } = await store.dispatch(getAllBackgroundsByProjectId(PROJECT_ID));
+    const { type, payload } = await store.dispatch(getAllBackgroundsByProjectId(PROJECT_ID));
     const { data, loading, error } = store.getState().backgrounds;
 
     expect(type).toBe('backgrounds/getAllBackgroundsByProjectId/rejected');
     expect(loading).toEqual('failed');
     expect(error).toEqual({ message: '', name: '' });
     expect(data).toEqual([]);
+    expect(payload).toBe(
+      "Failed to fetch backgrounds: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
   });
 
   it('should update store on loading getAllBackgroundsByProjectId query', async () => {
diff --git a/src/redux/backgrounds/backgrounds.thunks.ts b/src/redux/backgrounds/backgrounds.thunks.ts
index 3741a1c8..18a0c56b 100644
--- a/src/redux/backgrounds/backgrounds.thunks.ts
+++ b/src/redux/backgrounds/backgrounds.thunks.ts
@@ -4,17 +4,26 @@ import { MapBackground } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { apiPath } from '../apiPath';
+import { BACKGROUNDS_FETCHING_ERROR_PREFIX } from './backgrounds.constants';
 
-export const getAllBackgroundsByProjectId = createAsyncThunk(
+export const getAllBackgroundsByProjectId = createAsyncThunk<MapBackground[], string, ThunkConfig>(
   'backgrounds/getAllBackgroundsByProjectId',
-  async (projectId: string): Promise<MapBackground[]> => {
-    const response = await axiosInstance.get<MapBackground[]>(
-      apiPath.getAllBackgroundsByProjectIdQuery(projectId),
-    );
+  async (projectId: string, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstance.get<MapBackground[]>(
+        apiPath.getAllBackgroundsByProjectIdQuery(projectId),
+      );
 
-    const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapBackground));
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapBackground));
 
-    return isDataValid ? response.data : [];
+      return isDataValid ? response.data : [];
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: BACKGROUNDS_FETCHING_ERROR_PREFIX });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/bioEntity/bioEntity.constants.ts b/src/redux/bioEntity/bioEntity.constants.ts
index 3c109ae8..b719afde 100644
--- a/src/redux/bioEntity/bioEntity.constants.ts
+++ b/src/redux/bioEntity/bioEntity.constants.ts
@@ -1,3 +1,6 @@
 export const DEFAULT_BIOENTITY_PARAMS = {
   perfectMatch: false,
 };
+
+export const BIO_ENTITY_FETCHING_ERROR_PREFIX = 'Failed to fetch bio entity';
+export const MULTI_BIO_ENTITY_FETCHING_ERROR_PREFIX = 'Failed to fetch multi bio entity';
diff --git a/src/redux/bioEntity/bioEntity.reducers.test.ts b/src/redux/bioEntity/bioEntity.reducers.test.ts
index 52062b40..78482731 100644
--- a/src/redux/bioEntity/bioEntity.reducers.test.ts
+++ b/src/redux/bioEntity/bioEntity.reducers.test.ts
@@ -72,7 +72,7 @@ describe('bioEntity reducer', () => {
       )
       .reply(HttpStatusCode.NotFound, bioEntityResponseFixture);
 
-    const { type } = await store.dispatch(
+    const { type, payload } = await store.dispatch(
       getBioEntity({
         searchQuery: SEARCH_QUERY,
         isPerfectMatch: false,
@@ -84,6 +84,9 @@ describe('bioEntity reducer', () => {
       bioEntity => bioEntity.searchQueryElement === SEARCH_QUERY,
     );
     expect(type).toBe('project/getBioEntityContents/rejected');
+    expect(payload).toBe(
+      "Failed to fetch bio entity: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     expect(bioEnityWithSearchElement).toEqual({
       searchQueryElement: SEARCH_QUERY,
       data: undefined,
diff --git a/src/redux/bioEntity/bioEntity.thunks.test.ts b/src/redux/bioEntity/bioEntity.thunks.test.ts
index 9757ab4b..f2b54f3a 100644
--- a/src/redux/bioEntity/bioEntity.thunks.test.ts
+++ b/src/redux/bioEntity/bioEntity.thunks.test.ts
@@ -55,6 +55,26 @@ describe('bioEntityContents thunks', () => {
       );
       expect(payload).toEqual(undefined);
     });
+    it('should handle error message when getBioEntityContents failed', async () => {
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: SEARCH_QUERY,
+            isPerfectMatch: false,
+          }),
+        )
+        .reply(HttpStatusCode.NotFound, null);
+
+      const { payload } = await store.dispatch(
+        getBioEntity({
+          searchQuery: SEARCH_QUERY,
+          isPerfectMatch: false,
+        }),
+      );
+      expect(payload).toEqual(
+        "Failed to fetch bio entity: The page you're looking for doesn't exist. Please verify the URL and try again.",
+      );
+    });
   });
   describe('getMultiBioEntity', () => {
     it('should return transformed bioEntityContent array', async () => {
diff --git a/src/redux/bioEntity/bioEntity.thunks.ts b/src/redux/bioEntity/bioEntity.thunks.ts
index 32185755..20c312f5 100644
--- a/src/redux/bioEntity/bioEntity.thunks.ts
+++ b/src/redux/bioEntity/bioEntity.thunks.ts
@@ -5,15 +5,21 @@ import { apiPath } from '@/redux/apiPath';
 import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
 import { BioEntityContent, BioEntityResponse } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
+import {
+  BIO_ENTITY_FETCHING_ERROR_PREFIX,
+  MULTI_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+} from './bioEntity.constants';
 
 type GetBioEntityProps = PerfectSearchParams;
 
-export const getBioEntity = createAsyncThunk(
-  'project/getBioEntityContents',
-  async ({
-    searchQuery,
-    isPerfectMatch,
-  }: GetBioEntityProps): Promise<BioEntityContent[] | undefined> => {
+export const getBioEntity = createAsyncThunk<
+  BioEntityContent[] | undefined,
+  GetBioEntityProps,
+  ThunkConfig
+>('project/getBioEntityContents', async ({ searchQuery, isPerfectMatch }, { rejectWithValue }) => {
+  try {
     const response = await axiosInstanceNewAPI.get<BioEntityResponse>(
       apiPath.getBioEntityContentsStringWithQuery({ searchQuery, isPerfectMatch }),
     );
@@ -21,30 +27,47 @@ export const getBioEntity = createAsyncThunk(
     const isDataValid = validateDataUsingZodSchema(response.data, bioEntityResponseSchema);
 
     return isDataValid ? response.data.content : undefined;
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({
+      error,
+      prefix: BIO_ENTITY_FETCHING_ERROR_PREFIX,
+    });
+    return rejectWithValue(errorMessage);
+  }
+});
 
 type GetMultiBioEntityProps = PerfectMultiSearchParams;
 type GetMultiBioEntityActions = PayloadAction<BioEntityContent[] | undefined>[];
 
-export const getMultiBioEntity = createAsyncThunk(
+export const getMultiBioEntity = createAsyncThunk<
+  BioEntityContent[],
+  GetMultiBioEntityProps,
+  ThunkConfig
+>(
   'project/getMultiBioEntity',
-  async (
-    { searchQueries, isPerfectMatch }: GetMultiBioEntityProps,
-    { dispatch },
-  ): Promise<BioEntityContent[]> => {
-    const asyncGetBioEntityFunctions = searchQueries.map(searchQuery =>
-      dispatch(getBioEntity({ searchQuery, isPerfectMatch })),
-    );
+  // eslint-disable-next-line consistent-return
+  async ({ searchQueries, isPerfectMatch }, { dispatch, rejectWithValue }) => {
+    try {
+      const asyncGetBioEntityFunctions = searchQueries.map(searchQuery =>
+        dispatch(getBioEntity({ searchQuery, isPerfectMatch })),
+      );
+
+      const bioEntityContentsActions = (await Promise.all(
+        asyncGetBioEntityFunctions,
+      )) as GetMultiBioEntityActions;
 
-    const bioEntityContentsActions = (await Promise.all(
-      asyncGetBioEntityFunctions,
-    )) as GetMultiBioEntityActions;
+      const bioEntityContents = bioEntityContentsActions
+        .map(bioEntityContentsAction => bioEntityContentsAction.payload || [])
+        .flat();
 
-    const bioEntityContents = bioEntityContentsActions
-      .map(bioEntityContentsAction => bioEntityContentsAction.payload || [])
-      .flat();
+      return bioEntityContents;
+    } catch (error) {
+      const errorMessage = getErrorMessage({
+        error,
+        prefix: MULTI_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+      });
 
-    return bioEntityContents;
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/chemicals/chemicals.constants.ts b/src/redux/chemicals/chemicals.constants.ts
new file mode 100644
index 00000000..3311122e
--- /dev/null
+++ b/src/redux/chemicals/chemicals.constants.ts
@@ -0,0 +1,2 @@
+export const CHEMICALS_FETCHING_ERROR_PREFIX = 'Failed to fetch chemicals';
+export const MULTI_CHEMICALS_FETCHING_ERROR_PREFIX = 'Failed to fetch multi chemicals';
diff --git a/src/redux/chemicals/chemicals.reducers.test.ts b/src/redux/chemicals/chemicals.reducers.test.ts
index 59a87b20..9b66afe3 100644
--- a/src/redux/chemicals/chemicals.reducers.test.ts
+++ b/src/redux/chemicals/chemicals.reducers.test.ts
@@ -57,7 +57,7 @@ describe('chemicals reducer', () => {
       .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
       .reply(HttpStatusCode.NotFound, chemicalsFixture);
 
-    const { type } = await store.dispatch(getChemicals(SEARCH_QUERY));
+    const { type, payload } = await store.dispatch(getChemicals(SEARCH_QUERY));
     const { data } = store.getState().chemicals;
 
     const chemicalsWithSearchElement = data.find(
@@ -65,6 +65,9 @@ describe('chemicals reducer', () => {
     );
 
     expect(type).toBe('project/getChemicals/rejected');
+    expect(payload).toBe(
+      "Failed to fetch chemicals: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     expect(chemicalsWithSearchElement).toEqual({
       searchQueryElement: SEARCH_QUERY,
       data: undefined,
diff --git a/src/redux/chemicals/chemicals.thunks.test.ts b/src/redux/chemicals/chemicals.thunks.test.ts
index 88926792..73cb2d0e 100644
--- a/src/redux/chemicals/chemicals.thunks.test.ts
+++ b/src/redux/chemicals/chemicals.thunks.test.ts
@@ -35,5 +35,15 @@ describe('chemicals thunks', () => {
       const { payload } = await store.dispatch(getChemicals(SEARCH_QUERY));
       expect(payload).toEqual(undefined);
     });
+    it('should handle error message when getChemiclas failed ', async () => {
+      mockedAxiosClient
+        .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Forbidden, { randomProperty: 'randomValue' });
+
+      const { payload } = await store.dispatch(getChemicals(SEARCH_QUERY));
+      expect(payload).toEqual(
+        "Failed to fetch chemicals: Access Forbidden! You don't have permission to access this resource.",
+      );
+    });
   });
 });
diff --git a/src/redux/chemicals/chemicals.thunks.ts b/src/redux/chemicals/chemicals.thunks.ts
index 0afc94db..5b25951d 100644
--- a/src/redux/chemicals/chemicals.thunks.ts
+++ b/src/redux/chemicals/chemicals.thunks.ts
@@ -2,30 +2,51 @@ import { chemicalSchema } from '@/models/chemicalSchema';
 import { apiPath } from '@/redux/apiPath';
 import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
 import { Chemical } from '@/types/models';
+import { getErrorMessage } from '@/utils/getErrorMessage';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
+import { ThunkConfig } from '@/types/store';
+import {
+  CHEMICALS_FETCHING_ERROR_PREFIX,
+  MULTI_CHEMICALS_FETCHING_ERROR_PREFIX,
+} from './chemicals.constants';
 
-export const getChemicals = createAsyncThunk(
+export const getChemicals = createAsyncThunk<Chemical[] | undefined, string, ThunkConfig>(
   'project/getChemicals',
-  async (searchQuery: string): Promise<Chemical[] | undefined> => {
-    const response = await axiosInstanceNewAPI.get<Chemical[]>(
-      apiPath.getChemicalsStringWithQuery(searchQuery),
-    );
+  async (searchQuery, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstanceNewAPI.get<Chemical[]>(
+        apiPath.getChemicalsStringWithQuery(searchQuery),
+      );
 
-    const isDataValid = validateDataUsingZodSchema(response.data, z.array(chemicalSchema));
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(chemicalSchema));
 
-    return isDataValid ? response.data : undefined;
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: CHEMICALS_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
-export const getMultiChemicals = createAsyncThunk(
+export const getMultiChemicals = createAsyncThunk<void, string[], ThunkConfig>(
   'project/getMultChemicals',
-  async (searchQueries: string[], { dispatch }): Promise<void> => {
-    const asyncGetChemicalsFunctions = searchQueries.map(searchQuery =>
-      dispatch(getChemicals(searchQuery)),
-    );
+  // eslint-disable-next-line consistent-return
+  async (searchQueries, { dispatch, rejectWithValue }) => {
+    try {
+      const asyncGetChemicalsFunctions = searchQueries.map(searchQuery =>
+        dispatch(getChemicals(searchQuery)),
+      );
 
-    await Promise.all(asyncGetChemicalsFunctions);
+      await Promise.all(asyncGetChemicalsFunctions);
+    } catch (error) {
+      const errorMessage = getErrorMessage({
+        error,
+        prefix: MULTI_CHEMICALS_FETCHING_ERROR_PREFIX,
+      });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/compartmentPathways/comparmentPathways.constants.ts b/src/redux/compartmentPathways/comparmentPathways.constants.ts
index 2bf4d519..cc54322a 100644
--- a/src/redux/compartmentPathways/comparmentPathways.constants.ts
+++ b/src/redux/compartmentPathways/comparmentPathways.constants.ts
@@ -1 +1,3 @@
 export const MAX_NUMBER_OF_IDS_IN_GET_QUERY = 100;
+
+export const COMPARMENT_PATHWAYS_FETCHING_ERROR_PREFIX = 'Failed to fetch compartment pathways';
diff --git a/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts b/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts
index 037eefa2..94d445b8 100644
--- a/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts
+++ b/src/redux/compartmentPathways/compartmentPathways.reducers.test.ts
@@ -112,8 +112,11 @@ describe('compartmentPathways reducer', () => {
     expect(loading).toEqual('pending');
     expect(data).toEqual([]);
 
-    await compartmentPathwaysPromise;
+    const dispatchData = await compartmentPathwaysPromise;
 
+    expect(dispatchData.payload).toBe(
+      "Failed to fetch compartment pathways: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     const { loading: promiseFulfilled, data: dataFulfilled } = store.getState().compartmentPathways;
 
     expect(promiseFulfilled).toEqual('failed');
diff --git a/src/redux/compartmentPathways/compartmentPathways.thunks.ts b/src/redux/compartmentPathways/compartmentPathways.thunks.ts
index e0d69617..b8143dbe 100644
--- a/src/redux/compartmentPathways/compartmentPathways.thunks.ts
+++ b/src/redux/compartmentPathways/compartmentPathways.thunks.ts
@@ -8,7 +8,11 @@ import {
   compartmentPathwaySchema,
 } from '@/models/compartmentPathwaySchema';
 import { z } from 'zod';
-import { MAX_NUMBER_OF_IDS_IN_GET_QUERY } from './comparmentPathways.constants';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import {
+  COMPARMENT_PATHWAYS_FETCHING_ERROR_PREFIX,
+  MAX_NUMBER_OF_IDS_IN_GET_QUERY,
+} from './comparmentPathways.constants';
 import { apiPath } from '../apiPath';
 
 /** UTILS */
@@ -112,9 +116,18 @@ export const fetchCompartmentPathways = async (
 
 export const getCompartmentPathways = createAsyncThunk(
   'compartmentPathways/getCompartmentPathways',
-  async (modelsIds: number[] | undefined) => {
-    const compartmentIds = await fetchCompartmentPathwaysIds(modelsIds);
-    const comparmentPathways = await fetchCompartmentPathways(compartmentIds);
-    return comparmentPathways;
+  async (modelsIds: number[] | undefined, { rejectWithValue }) => {
+    try {
+      const compartmentIds = await fetchCompartmentPathwaysIds(modelsIds);
+      const comparmentPathways = await fetchCompartmentPathways(compartmentIds);
+
+      return comparmentPathways;
+    } catch (error) {
+      const errorMessage = getErrorMessage({
+        error,
+        prefix: COMPARMENT_PATHWAYS_FETCHING_ERROR_PREFIX,
+      });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/configuration/configuration.constants.ts b/src/redux/configuration/configuration.constants.ts
index bcf3c906..9a2762d9 100644
--- a/src/redux/configuration/configuration.constants.ts
+++ b/src/redux/configuration/configuration.constants.ts
@@ -19,3 +19,7 @@ export const SBGN_ML_HANDLER_NAME_ID = 'SBGN-ML';
 export const PNG_IMAGE_HANDLER_NAME_ID = 'PNG image';
 export const PDF_HANDLER_NAME_ID = 'PDF';
 export const SVG_IMAGE_HANDLER_NAME_ID = 'SVG image';
+
+export const CONFIGURATION_OPTIONS_FETCHING_ERROR_PREFIX = 'Failed to fetch configuration options';
+
+export const CONFIGURATION_FETCHING_ERROR_PREFIX = 'Failed to fetch configuration';
diff --git a/src/redux/configuration/configuration.thunks.ts b/src/redux/configuration/configuration.thunks.ts
index 012e8b1c..8b4db5ea 100644
--- a/src/redux/configuration/configuration.thunks.ts
+++ b/src/redux/configuration/configuration.thunks.ts
@@ -5,11 +5,20 @@ import { axiosInstance } from '@/services/api/utils/axiosInstance';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { configurationSchema } from '@/models/configurationSchema';
 import { configurationOptionSchema } from '@/models/configurationOptionSchema';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { apiPath } from '../apiPath';
+import {
+  CONFIGURATION_FETCHING_ERROR_PREFIX,
+  CONFIGURATION_OPTIONS_FETCHING_ERROR_PREFIX,
+} from './configuration.constants';
 
-export const getConfigurationOptions = createAsyncThunk(
-  'configuration/getConfigurationOptions',
-  async (): Promise<ConfigurationOption[] | undefined> => {
+export const getConfigurationOptions = createAsyncThunk<
+  ConfigurationOption[] | undefined,
+  void,
+  ThunkConfig
+>('configuration/getConfigurationOptions', async (_, { rejectWithValue }) => {
+  try {
     const response = await axiosInstance.get<ConfigurationOption[]>(
       apiPath.getConfigurationOptions(),
     );
@@ -20,16 +29,27 @@ export const getConfigurationOptions = createAsyncThunk(
     );
 
     return isDataValid ? response.data : undefined;
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({
+      error,
+      prefix: CONFIGURATION_OPTIONS_FETCHING_ERROR_PREFIX,
+    });
+    return rejectWithValue(errorMessage);
+  }
+});
 
-export const getConfiguration = createAsyncThunk(
+export const getConfiguration = createAsyncThunk<Configuration | undefined, void, ThunkConfig>(
   'configuration/getConfiguration',
-  async (): Promise<Configuration | undefined> => {
-    const response = await axiosInstance.get<Configuration>(apiPath.getConfiguration());
+  async (_, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstance.get<Configuration>(apiPath.getConfiguration());
 
-    const isDataValid = validateDataUsingZodSchema(response.data, configurationSchema);
+      const isDataValid = validateDataUsingZodSchema(response.data, configurationSchema);
 
-    return isDataValid ? response.data : undefined;
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: CONFIGURATION_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/drawer/drawer.constants.ts b/src/redux/drawer/drawer.constants.ts
index 33890a23..bef3fd3d 100644
--- a/src/redux/drawer/drawer.constants.ts
+++ b/src/redux/drawer/drawer.constants.ts
@@ -19,3 +19,7 @@ export const DRAWER_INITIAL_STATE: DrawerState = {
     currentStep: 0,
   },
 };
+
+export const DRUGS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX = 'Failed to fetch drugs for bio entity';
+export const CHEMICALS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX =
+  'Failed to fetch chemicals for bio entity';
diff --git a/src/redux/drawer/drawer.thunks.ts b/src/redux/drawer/drawer.thunks.ts
index 7e60536f..f08955f9 100644
--- a/src/redux/drawer/drawer.thunks.ts
+++ b/src/redux/drawer/drawer.thunks.ts
@@ -5,7 +5,13 @@ import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
 import { Chemical, Drug, TargetSearchNameResult } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
+import { ThunkConfig } from '@/types/store';
+import { getErrorMessage } from '@/utils/getErrorMessage';
 import { apiPath } from '../apiPath';
+import {
+  CHEMICALS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+  DRUGS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+} from './drawer.constants';
 
 const QUERY_COLUMN_NAME = 'name';
 
@@ -28,16 +34,25 @@ const getDrugsByName = async (drugName: string): Promise<Drug[]> => {
   return response.data.filter(isDataValid);
 };
 
-export const getDrugsForBioEntityDrawerTarget = createAsyncThunk(
+export const getDrugsForBioEntityDrawerTarget = createAsyncThunk<Drug[], string, ThunkConfig>(
   'drawer/getDrugsForBioEntityDrawerTarget',
-  async (target: string): Promise<Drug[]> => {
-    const drugsNames = await getDrugsNamesForTarget(target);
-    const drugsArrays = await Promise.all(
-      drugsNames.map(({ name }) => getDrugsByName(encodeURIComponent(name))),
-    );
-    const drugs = drugsArrays.flat();
-
-    return drugs;
+  async (target, { rejectWithValue }) => {
+    try {
+      const drugsNames = await getDrugsNamesForTarget(target);
+      const drugsArrays = await Promise.all(
+        drugsNames.map(({ name }) => getDrugsByName(encodeURIComponent(name))),
+      );
+      const drugs = drugsArrays.flat();
+
+      return drugs;
+    } catch (error) {
+      const errorMessage = getErrorMessage({
+        error,
+        prefix: DRUGS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+      });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
@@ -63,9 +78,12 @@ const getChemicalsByName = async (chemicalName: string): Promise<Chemical[]> =>
   return response.data.filter(isDataValid);
 };
 
-export const getChemicalsForBioEntityDrawerTarget = createAsyncThunk(
-  'drawer/getChemicalsForBioEntityDrawerTarget',
-  async (target: string): Promise<Chemical[]> => {
+export const getChemicalsForBioEntityDrawerTarget = createAsyncThunk<
+  Chemical[],
+  string,
+  ThunkConfig
+>('drawer/getChemicalsForBioEntityDrawerTarget', async (target, { rejectWithValue }) => {
+  try {
     const chemicalsNames = await getChemicalsNamesForTarget(target);
     const chemicalsArrays = await Promise.all(
       chemicalsNames.map(({ name }) => getChemicalsByName(encodeURIComponent(name))),
@@ -73,5 +91,12 @@ export const getChemicalsForBioEntityDrawerTarget = createAsyncThunk(
     const chemicals = chemicalsArrays.flat();
 
     return chemicals;
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({
+      error,
+      prefix: CHEMICALS_FOR_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+    });
+
+    return rejectWithValue(errorMessage);
+  }
+});
diff --git a/src/redux/drugs/drugs.constants.ts b/src/redux/drugs/drugs.constants.ts
new file mode 100644
index 00000000..c31cff0f
--- /dev/null
+++ b/src/redux/drugs/drugs.constants.ts
@@ -0,0 +1,2 @@
+export const DRUGS_FETCHING_ERROR_PREFIX = 'Failed to fetch drugs';
+export const MULTI_DRUGS_FETCHING_ERROR_PREFIX = 'Failed to fetch multi drugs';
diff --git a/src/redux/drugs/drugs.reducers.test.ts b/src/redux/drugs/drugs.reducers.test.ts
index 3ad034db..f6e6c671 100644
--- a/src/redux/drugs/drugs.reducers.test.ts
+++ b/src/redux/drugs/drugs.reducers.test.ts
@@ -56,12 +56,15 @@ describe('drugs reducer', () => {
       .onGet(apiPath.getDrugsStringWithQuery(SEARCH_QUERY))
       .reply(HttpStatusCode.NotFound, []);
 
-    const { type } = await store.dispatch(getDrugs(SEARCH_QUERY));
+    const { type, payload } = await store.dispatch(getDrugs(SEARCH_QUERY));
     const { data } = store.getState().drugs;
     const drugsWithSearchElement = data.find(
       bioEntity => bioEntity.searchQueryElement === SEARCH_QUERY,
     );
 
+    expect(payload).toBe(
+      "Failed to fetch drugs: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     expect(type).toBe('project/getDrugs/rejected');
     expect(drugsWithSearchElement).toEqual({
       searchQueryElement: SEARCH_QUERY,
diff --git a/src/redux/drugs/drugs.thunks.ts b/src/redux/drugs/drugs.thunks.ts
index 30074e33..6bd3b532 100644
--- a/src/redux/drugs/drugs.thunks.ts
+++ b/src/redux/drugs/drugs.thunks.ts
@@ -2,30 +2,45 @@ import { drugSchema } from '@/models/drugSchema';
 import { apiPath } from '@/redux/apiPath';
 import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
 import { Drug } from '@/types/models';
+import { getErrorMessage } from '@/utils/getErrorMessage';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
+import { ThunkConfig } from '@/types/store';
+import { DRUGS_FETCHING_ERROR_PREFIX, MULTI_DRUGS_FETCHING_ERROR_PREFIX } from './drugs.constants';
 
-export const getDrugs = createAsyncThunk(
+export const getDrugs = createAsyncThunk<Drug[] | undefined, string, ThunkConfig>(
   'project/getDrugs',
-  async (searchQuery: string): Promise<Drug[] | undefined> => {
-    const response = await axiosInstanceNewAPI.get<Drug[]>(
-      apiPath.getDrugsStringWithQuery(searchQuery),
-    );
+  async (searchQuery: string, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstanceNewAPI.get<Drug[]>(
+        apiPath.getDrugsStringWithQuery(searchQuery),
+      );
 
-    const isDataValid = validateDataUsingZodSchema(response.data, z.array(drugSchema));
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(drugSchema));
 
-    return isDataValid ? response.data : undefined;
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: DRUGS_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
-export const getMultiDrugs = createAsyncThunk(
+export const getMultiDrugs = createAsyncThunk<void, string[], ThunkConfig>(
   'project/getMultiDrugs',
-  async (searchQueries: string[], { dispatch }): Promise<void> => {
-    const asyncGetDrugsFunctions = searchQueries.map(searchQuery =>
-      dispatch(getDrugs(searchQuery)),
-    );
+  // eslint-disable-next-line consistent-return
+  async (searchQueries, { dispatch, rejectWithValue }) => {
+    try {
+      const asyncGetDrugsFunctions = searchQueries.map(searchQuery =>
+        dispatch(getDrugs(searchQuery)),
+      );
 
-    await Promise.all(asyncGetDrugsFunctions);
+      await Promise.all(asyncGetDrugsFunctions);
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: MULTI_DRUGS_FETCHING_ERROR_PREFIX });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/export/export.constants.ts b/src/redux/export/export.constants.ts
new file mode 100644
index 00000000..8f464a4d
--- /dev/null
+++ b/src/redux/export/export.constants.ts
@@ -0,0 +1,2 @@
+export const ELEMENTS_DOWNLOAD_ERROR_PREFIX = 'Failed to download elements';
+export const NETWORK_DOWNLOAD_ERROR_PREFIX = 'Failed to download network';
diff --git a/src/redux/export/export.reducers.test.ts b/src/redux/export/export.reducers.test.ts
index 894ee98a..778aca4f 100644
--- a/src/redux/export/export.reducers.test.ts
+++ b/src/redux/export/export.reducers.test.ts
@@ -76,7 +76,7 @@ describe('export reducer', () => {
     mockedAxiosClient
       .onPost(apiPath.downloadNetworkCsv())
       .reply(HttpStatusCode.NotFound, undefined);
-    await store.dispatch(
+    const { payload, type } = await store.dispatch(
       downloadNetwork({
         annotations: [],
         columns: [],
@@ -85,8 +85,11 @@ describe('export reducer', () => {
         submaps: [],
       }),
     );
+    expect(type).toBe('export/downloadNetwork/rejected');
+    expect(payload).toBe(
+      "Failed to download network: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     const { loading } = store.getState().export.downloadNetwork;
-
     expect(loading).toEqual('failed');
   });
 
@@ -132,7 +135,7 @@ describe('export reducer', () => {
     mockedAxiosClient
       .onPost(apiPath.downloadElementsCsv())
       .reply(HttpStatusCode.NotFound, undefined);
-    await store.dispatch(
+    const { payload } = await store.dispatch(
       downloadElements({
         annotations: [],
         columns: [],
@@ -144,5 +147,8 @@ describe('export reducer', () => {
     const { loading } = store.getState().export.downloadElements;
 
     expect(loading).toEqual('failed');
+    expect(payload).toEqual(
+      "Failed to download elements: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
   });
 });
diff --git a/src/redux/export/export.thunks.ts b/src/redux/export/export.thunks.ts
index 3a25cd8c..bf49e0b0 100644
--- a/src/redux/export/export.thunks.ts
+++ b/src/redux/export/export.thunks.ts
@@ -5,8 +5,11 @@ import { PROJECT_ID } from '@/constants';
 import { ExportNetwork, ExportElements } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { exportNetworkchema, exportElementsSchema } from '@/models/exportSchema';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { apiPath } from '../apiPath';
 import { downloadFileFromBlob } from './export.utils';
+import { ELEMENTS_DOWNLOAD_ERROR_PREFIX, NETWORK_DOWNLOAD_ERROR_PREFIX } from './export.constants';
 
 type DownloadElementsBodyRequest = {
   columns: string[];
@@ -16,9 +19,13 @@ type DownloadElementsBodyRequest = {
   excludedCompartmentIds: number[];
 };
 
-export const downloadElements = createAsyncThunk(
-  'export/downloadElements',
-  async (data: DownloadElementsBodyRequest): Promise<void> => {
+export const downloadElements = createAsyncThunk<
+  undefined,
+  DownloadElementsBodyRequest,
+  ThunkConfig
+  // eslint-disable-next-line consistent-return
+>('export/downloadElements', async (data, { rejectWithValue }) => {
+  try {
     const response = await axiosInstanceNewAPI.post<ExportElements>(
       apiPath.downloadElementsCsv(),
       data,
@@ -32,8 +39,12 @@ export const downloadElements = createAsyncThunk(
     if (isDataValid) {
       downloadFileFromBlob(response.data, `${PROJECT_ID}-elementExport.csv`);
     }
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: ELEMENTS_DOWNLOAD_ERROR_PREFIX });
+
+    return rejectWithValue(errorMessage);
+  }
+});
 
 type DownloadNetworkBodyRequest = {
   columns: string[];
@@ -43,21 +54,28 @@ type DownloadNetworkBodyRequest = {
   excludedCompartmentIds: number[];
 };
 
-export const downloadNetwork = createAsyncThunk(
+export const downloadNetwork = createAsyncThunk<undefined, DownloadNetworkBodyRequest, ThunkConfig>(
   'export/downloadNetwork',
-  async (data: DownloadNetworkBodyRequest): Promise<void> => {
-    const response = await axiosInstanceNewAPI.post<ExportNetwork>(
-      apiPath.downloadNetworkCsv(),
-      data,
-      {
-        withCredentials: true,
-      },
-    );
+  // eslint-disable-next-line consistent-return
+  async (data, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstanceNewAPI.post<ExportNetwork>(
+        apiPath.downloadNetworkCsv(),
+        data,
+        {
+          withCredentials: true,
+        },
+      );
 
-    const isDataValid = validateDataUsingZodSchema(response.data, exportNetworkchema);
+      const isDataValid = validateDataUsingZodSchema(response.data, exportNetworkchema);
 
-    if (isDataValid) {
-      downloadFileFromBlob(response.data, `${PROJECT_ID}-networkExport.csv`);
+      if (isDataValid) {
+        downloadFileFromBlob(response.data, `${PROJECT_ID}-networkExport.csv`);
+      }
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: NETWORK_DOWNLOAD_ERROR_PREFIX });
+
+      return rejectWithValue(errorMessage);
     }
   },
 );
diff --git a/src/redux/map/map.constants.ts b/src/redux/map/map.constants.ts
index 27a63ef2..70126c71 100644
--- a/src/redux/map/map.constants.ts
+++ b/src/redux/map/map.constants.ts
@@ -54,3 +54,11 @@ export const MAP_INITIAL_STATE: MapState = {
   error: { name: '', message: '' },
   openedMaps: OPENED_MAPS_INITIAL_STATE,
 };
+
+export const INIT_MAP_SIZE_MODEL_ID_ERROR_PREFIX = 'Failed to initialize map size and model ID';
+
+export const INIT_MAP_POSITION_ERROR_PREFIX = 'Failed to initialize map position';
+
+export const INIT_MAP_BACKGROUND_ERROR_PREFIX = 'Failed to initialize map background';
+
+export const INIT_OPENED_MAPS_ERROR_PREFIX = 'Failed to initialize opened maps';
diff --git a/src/redux/map/map.thunks.ts b/src/redux/map/map.thunks.ts
index e9c3c7ae..05b675bb 100644
--- a/src/redux/map/map.thunks.ts
+++ b/src/redux/map/map.thunks.ts
@@ -5,6 +5,8 @@ import { QueryData } from '@/types/query';
 import { DEFAULT_ZOOM } from '@/constants/map';
 import { getPointMerged } from '@/utils/object/getPointMerged';
 import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import type { AppDispatch, RootState } from '../store';
 import {
   InitMapBackgroundActionPayload,
@@ -26,7 +28,14 @@ import {
   modelByIdSelector,
   modelsDataSelector,
 } from '../models/models.selectors';
-import { DEFAULT_POSITION, MAIN_MAP } from './map.constants';
+import {
+  DEFAULT_POSITION,
+  MAIN_MAP,
+  INIT_MAP_BACKGROUND_ERROR_PREFIX,
+  INIT_MAP_POSITION_ERROR_PREFIX,
+  INIT_MAP_SIZE_MODEL_ID_ERROR_PREFIX,
+  INIT_OPENED_MAPS_ERROR_PREFIX,
+} from './map.constants';
 
 /** UTILS - in the same file because of dependancy cycle */
 
@@ -135,47 +144,62 @@ export const getOpenedMaps = (state: RootState, queryData: QueryData): OppenedMa
 export const initMapSizeAndModelId = createAsyncThunk<
   InitMapSizeAndModelIdActionPayload,
   InitMapSizeAndModelIdParams,
-  { dispatch: AppDispatch; state: RootState }
->(
-  'map/initMapSizeAndModelId',
-  async ({ queryData }, { getState }): Promise<InitMapSizeAndModelIdActionPayload> => {
+  { dispatch: AppDispatch; state: RootState } & ThunkConfig
+>('map/initMapSizeAndModelId', async ({ queryData }, { getState, rejectWithValue }) => {
+  try {
     const state = getState();
 
     return getInitMapSizeAndModelId(state, queryData);
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: INIT_MAP_SIZE_MODEL_ID_ERROR_PREFIX });
+
+    return rejectWithValue(errorMessage);
+  }
+});
 
 export const initMapPosition = createAsyncThunk<
   InitMapPositionActionPayload,
   InitMapPositionParams,
-  { dispatch: AppDispatch; state: RootState }
->(
-  'map/initMapPosition',
-  async ({ queryData }, { getState }): Promise<InitMapPositionActionPayload> => {
+  { dispatch: AppDispatch; state: RootState } & ThunkConfig
+>('map/initMapPosition', async ({ queryData }, { getState, rejectWithValue }) => {
+  try {
     const state = getState();
 
     return getInitMapPosition(state, queryData);
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: INIT_MAP_POSITION_ERROR_PREFIX });
+
+    return rejectWithValue(errorMessage);
+  }
+});
 
 export const initMapBackground = createAsyncThunk<
   InitMapBackgroundActionPayload,
   InitMapBackgroundParams,
-  { dispatch: AppDispatch; state: RootState }
->(
-  'map/initMapBackground',
-  async ({ queryData }, { getState }): Promise<InitMapBackgroundActionPayload> => {
+  { dispatch: AppDispatch; state: RootState } & ThunkConfig
+>('map/initMapBackground', async ({ queryData }, { getState, rejectWithValue }) => {
+  try {
     const state = getState();
     return getBackgroundId(state, queryData);
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: INIT_MAP_BACKGROUND_ERROR_PREFIX });
+
+    return rejectWithValue(errorMessage);
+  }
+});
 
 export const initOpenedMaps = createAsyncThunk<
   InitOpenedMapsActionPayload,
   InitOpenedMapsProps,
-  { dispatch: AppDispatch; state: RootState }
->('appInit/initOpenedMaps', async ({ queryData }, { getState }): Promise<OppenedMap[]> => {
-  const state = getState();
+  { dispatch: AppDispatch; state: RootState } & ThunkConfig
+>('appInit/initOpenedMaps', async ({ queryData }, { getState, rejectWithValue }) => {
+  try {
+    const state = getState();
+
+    return getOpenedMaps(state, queryData);
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: INIT_OPENED_MAPS_ERROR_PREFIX });
 
-  return getOpenedMaps(state, queryData);
+    return rejectWithValue(errorMessage);
+  }
 });
diff --git a/src/redux/middlewares/error.middleware.test.ts b/src/redux/middlewares/error.middleware.test.ts
new file mode 100644
index 00000000..7662714e
--- /dev/null
+++ b/src/redux/middlewares/error.middleware.test.ts
@@ -0,0 +1,87 @@
+import { showToast } from '@/utils/showToast';
+import { errorMiddlewareListener } from './error.middleware';
+
+jest.mock('../../utils/showToast', () => ({
+  showToast: jest.fn(),
+}));
+
+describe('errorMiddlewareListener', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('should show toast with error message when action is rejected with value', async () => {
+    const action = {
+      type: 'action/rejected',
+      payload: 'Error message',
+      meta: {
+        requestId: '421',
+        rejectedWithValue: true,
+        requestStatus: 'rejected',
+      },
+    };
+    await errorMiddlewareListener(action);
+    expect(showToast).toHaveBeenCalledWith({ type: 'error', message: 'Error message' });
+  });
+
+  it('should show toast with unknown error when action is rejected without value', async () => {
+    const action = {
+      type: 'action/rejected',
+      payload: null,
+      meta: {
+        requestId: '421',
+        rejectedWithValue: true,
+        requestStatus: 'rejected',
+      },
+    };
+    await errorMiddlewareListener(action);
+    expect(showToast).toHaveBeenCalledWith({
+      type: 'error',
+      message: 'An unknown error occurred. Please try again later.',
+    });
+  });
+
+  it('should not show toast when action is not rejected', async () => {
+    const action = {
+      type: 'action/loading',
+      payload: null,
+      meta: {
+        requestId: '421',
+        requestStatus: 'fulfilled',
+      },
+    };
+    await errorMiddlewareListener(action);
+    expect(showToast).not.toHaveBeenCalled();
+  });
+
+  it('should show toast with unknown error when action payload is not a string', async () => {
+    const action = {
+      type: 'action/rejected',
+      payload: {},
+      meta: {
+        requestId: '421',
+        rejectedWithValue: true,
+        requestStatus: 'rejected',
+      },
+    };
+    await errorMiddlewareListener(action);
+    expect(showToast).toHaveBeenCalledWith({
+      type: 'error',
+      message: 'An unknown error occurred. Please try again later.',
+    });
+  });
+
+  it('should show toast with custom message when action payload is a string', async () => {
+    const action = {
+      type: 'action/rejected',
+      payload: 'Failed to fetch',
+      meta: {
+        requestId: '421',
+        rejectedWithValue: true,
+        requestStatus: 'rejected',
+      },
+    };
+    await errorMiddlewareListener(action);
+    expect(showToast).toHaveBeenCalledWith({ type: 'error', message: 'Failed to fetch' });
+  });
+});
diff --git a/src/redux/middlewares/error.middleware.ts b/src/redux/middlewares/error.middleware.ts
new file mode 100644
index 00000000..0ba6a0f7
--- /dev/null
+++ b/src/redux/middlewares/error.middleware.ts
@@ -0,0 +1,34 @@
+import type { AppStartListening } from '@/redux/store';
+import { UNKNOWN_ERROR } from '@/utils/getErrorMessage/getErrorMessage.constants';
+import { showToast } from '@/utils/showToast';
+import {
+  Action,
+  createListenerMiddleware,
+  isRejected,
+  isRejectedWithValue,
+} from '@reduxjs/toolkit';
+
+export const errorListenerMiddleware = createListenerMiddleware();
+
+const startListening = errorListenerMiddleware.startListening as AppStartListening;
+
+export const errorMiddlewareListener = async (action: Action): Promise<void> => {
+  if (isRejectedWithValue(action)) {
+    let message: string;
+    if (typeof action.payload === 'string') {
+      message = action.payload;
+    } else {
+      message = UNKNOWN_ERROR;
+    }
+
+    showToast({
+      type: 'error',
+      message,
+    });
+  }
+};
+
+startListening({
+  matcher: isRejected,
+  effect: errorMiddlewareListener,
+});
diff --git a/src/redux/models/models.constants.ts b/src/redux/models/models.constants.ts
new file mode 100644
index 00000000..8855ad36
--- /dev/null
+++ b/src/redux/models/models.constants.ts
@@ -0,0 +1 @@
+export const MODELS_FETCHING_ERROR_PREFIX = 'Failed to fetch models';
diff --git a/src/redux/models/models.reducers.test.ts b/src/redux/models/models.reducers.test.ts
index 1677afdf..fc6e0af1 100644
--- a/src/redux/models/models.reducers.test.ts
+++ b/src/redux/models/models.reducers.test.ts
@@ -44,10 +44,13 @@ describe('models reducer', () => {
   it('should update store after failed getModels query', async () => {
     mockedAxiosClient.onGet(apiPath.getModelsString()).reply(HttpStatusCode.NotFound, []);
 
-    const { type } = await store.dispatch(getModels());
+    const { type, payload } = await store.dispatch(getModels());
     const { data, loading, error } = store.getState().models;
 
     expect(type).toBe('project/getModels/rejected');
+    expect(payload).toBe(
+      "Failed to fetch models: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     expect(loading).toEqual('failed');
     expect(error).toEqual({ message: '', name: '' });
     expect(data).toEqual([]);
diff --git a/src/redux/models/models.thunks.ts b/src/redux/models/models.thunks.ts
index 5880ddcd..2e0fd68d 100644
--- a/src/redux/models/models.thunks.ts
+++ b/src/redux/models/models.thunks.ts
@@ -2,17 +2,26 @@ import { mapModelSchema } from '@/models/modelSchema';
 import { apiPath } from '@/redux/apiPath';
 import { axiosInstance } from '@/services/api/utils/axiosInstance';
 import { MapModel } from '@/types/models';
+import { getErrorMessage } from '@/utils/getErrorMessage';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
+import { ThunkConfig } from '@/types/store';
+import { MODELS_FETCHING_ERROR_PREFIX } from './models.constants';
 
-export const getModels = createAsyncThunk(
+export const getModels = createAsyncThunk<MapModel[] | undefined, void, ThunkConfig>(
   'project/getModels',
-  async (): Promise<MapModel[] | undefined> => {
-    const response = await axiosInstance.get<MapModel[]>(apiPath.getModelsString());
+  async (_, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstance.get<MapModel[]>(apiPath.getModelsString());
 
-    const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapModelSchema));
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapModelSchema));
 
-    return isDataValid ? response.data : undefined;
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: MODELS_FETCHING_ERROR_PREFIX });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/overlayBioEntity/overlayBioEntity.constants.ts b/src/redux/overlayBioEntity/overlayBioEntity.constants.ts
new file mode 100644
index 00000000..5b9a636a
--- /dev/null
+++ b/src/redux/overlayBioEntity/overlayBioEntity.constants.ts
@@ -0,0 +1,4 @@
+export const OVERLAY_BIO_ENTITY_FETCHING_ERROR_PREFIX = 'Failed to fetch overlay bio entity';
+export const OVERLAY_BIO_ENTITY_ALL_MODELS_FETCHING_ERROR_PREFIX =
+  'Failed to fetch overlay bio entity for all models';
+export const INIT_OVERLAYS_ERROR_PREFIX = 'Failed to initialize overlays';
diff --git a/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts b/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts
index 1fed7a97..30222f30 100644
--- a/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts
+++ b/src/redux/overlayBioEntity/overlayBioEntity.thunk.ts
@@ -3,6 +3,8 @@ import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
 import { OverlayBioEntity } from '@/types/models';
 import { OverlayBioEntityRender } from '@/types/OLrendering';
 import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import {
   getValidOverlayBioEntities,
   parseOverlayBioEntityToOlRenderingFormat,
@@ -13,18 +15,23 @@ import type { RootState } from '../store';
 import { setMapBackground } from '../map/map.slice';
 import { emptyBackgroundIdSelector } from '../backgrounds/background.selectors';
 import { overlaySelector, userOverlaySelector } from '../overlays/overlays.selectors';
+import {
+  INIT_OVERLAYS_ERROR_PREFIX,
+  OVERLAY_BIO_ENTITY_ALL_MODELS_FETCHING_ERROR_PREFIX,
+  OVERLAY_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+} from './overlayBioEntity.constants';
 
 type GetOverlayBioEntityThunkProps = {
   overlayId: number;
   modelId: number;
 };
 
-export const getOverlayBioEntity = createAsyncThunk(
-  'overlayBioEntity/getOverlayBioEntity',
-  async ({
-    overlayId,
-    modelId,
-  }: GetOverlayBioEntityThunkProps): Promise<OverlayBioEntityRender[] | undefined> => {
+export const getOverlayBioEntity = createAsyncThunk<
+  OverlayBioEntityRender[] | undefined,
+  GetOverlayBioEntityThunkProps,
+  ThunkConfig
+>('overlayBioEntity/getOverlayBioEntity', async ({ overlayId, modelId }, { rejectWithValue }) => {
+  try {
     const response = await axiosInstanceNewAPI.get<OverlayBioEntity[]>(
       apiPath.getOverlayBioEntity({ overlayId, modelId }),
       {
@@ -39,34 +46,55 @@ export const getOverlayBioEntity = createAsyncThunk(
     }
 
     return undefined;
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({
+      error,
+      prefix: OVERLAY_BIO_ENTITY_FETCHING_ERROR_PREFIX,
+    });
+
+    return rejectWithValue(errorMessage);
+  }
+});
 
 type GetOverlayBioEntityForAllModelsThunkProps = { overlayId: number };
 
 export const getOverlayBioEntityForAllModels = createAsyncThunk<
   void,
   GetOverlayBioEntityForAllModelsThunkProps,
-  { state: RootState }
+  { state: RootState } & ThunkConfig
 >(
   'overlayBioEntity/getOverlayBioEntityForAllModels',
-  async ({ overlayId }, { dispatch, getState }): Promise<void> => {
-    const state = getState();
-    const modelsIds = modelsIdsSelector(state);
+  // eslint-disable-next-line consistent-return
+  async ({ overlayId }, { dispatch, getState, rejectWithValue }) => {
+    try {
+      const state = getState();
+      const modelsIds = modelsIdsSelector(state);
 
-    const asyncGetOverlayBioEntityFunctions = modelsIds.map(id =>
-      dispatch(getOverlayBioEntity({ overlayId, modelId: id })),
-    );
+      const asyncGetOverlayBioEntityFunctions = modelsIds.map(id =>
+        dispatch(getOverlayBioEntity({ overlayId, modelId: id })),
+      );
 
-    await Promise.all(asyncGetOverlayBioEntityFunctions);
+      await Promise.all(asyncGetOverlayBioEntityFunctions);
+    } catch (error) {
+      const errorMessage = getErrorMessage({
+        error,
+        prefix: OVERLAY_BIO_ENTITY_ALL_MODELS_FETCHING_ERROR_PREFIX,
+      });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
 type GetInitOverlaysProps = { overlaysId: number[] };
 
-export const getInitOverlays = createAsyncThunk<void, GetInitOverlaysProps, { state: RootState }>(
-  'appInit/getInitOverlays',
-  async ({ overlaysId }, { dispatch, getState }): Promise<void> => {
+export const getInitOverlays = createAsyncThunk<
+  void,
+  GetInitOverlaysProps,
+  { state: RootState } & ThunkConfig
+  // eslint-disable-next-line consistent-return
+>('appInit/getInitOverlays', async ({ overlaysId }, { dispatch, getState, rejectWithValue }) => {
+  try {
     const state = getState();
 
     const emptyBackgroundId = emptyBackgroundIdSelector(state);
@@ -86,5 +114,9 @@ export const getInitOverlays = createAsyncThunk<void, GetInitOverlaysProps, { st
 
       dispatch(getOverlayBioEntityForAllModels({ overlayId: id }));
     });
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: INIT_OVERLAYS_ERROR_PREFIX });
+
+    return rejectWithValue(errorMessage);
+  }
+});
diff --git a/src/redux/overlays/overlays.constants.ts b/src/redux/overlays/overlays.constants.ts
index dda564cd..fa7efe22 100644
--- a/src/redux/overlays/overlays.constants.ts
+++ b/src/redux/overlays/overlays.constants.ts
@@ -1,2 +1,11 @@
 /* eslint-disable no-magic-numbers */
 export const CHUNK_SIZE = 65535 * 8;
+
+export const OVERLAYS_FETCHING_ERROR_PREFIX = 'Failed to fetch overlays';
+export const USER_OVERLAY_ADD_ERROR_PREFIX = 'Failed to add user overlay';
+export const USER_OVERLAY_ADD_SUCCESS_MESSAGE = 'User overlay added successfully';
+export const USER_OVERLAYS_FETCHING_ERROR_PREFIX = 'Failed to fetch user overlays';
+export const USER_OVERLAY_UPDATE_ERROR_PREFIX = 'Failed to update user overlay';
+export const USER_OVERLAY_UPDATE_SUCCESS_MESSAGE = 'User overlay updated successfully';
+export const USER_OVERLAY_REMOVE_ERROR_PREFIX = 'Failed to remove user overlay';
+export const USER_OVERLAY_REMOVE_SUCCESS_MESSAGE = 'User overlay removed successfully';
diff --git a/src/redux/overlays/overlays.reducers.test.ts b/src/redux/overlays/overlays.reducers.test.ts
index f774974f..38b4652f 100644
--- a/src/redux/overlays/overlays.reducers.test.ts
+++ b/src/redux/overlays/overlays.reducers.test.ts
@@ -80,10 +80,13 @@ describe('overlays reducer', () => {
       .onGet(apiPath.getAllOverlaysByProjectIdQuery(PROJECT_ID, { publicOverlay: true }))
       .reply(HttpStatusCode.NotFound, []);
 
-    const { type } = await store.dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID));
+    const { type, payload } = await store.dispatch(getAllPublicOverlaysByProjectId(PROJECT_ID));
     const { data, loading, error } = store.getState().overlays;
 
     expect(type).toBe('overlays/getAllPublicOverlaysByProjectId/rejected');
+    expect(payload).toBe(
+      "Failed to fetch overlays: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     expect(loading).toEqual('failed');
     expect(error).toEqual({ message: '', name: '' });
     expect(data).toEqual([]);
diff --git a/src/redux/overlays/overlays.thunks.ts b/src/redux/overlays/overlays.thunks.ts
index 63316421..4b1fa460 100644
--- a/src/redux/overlays/overlays.thunks.ts
+++ b/src/redux/overlays/overlays.thunks.ts
@@ -11,51 +11,75 @@ import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
 import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
+import { showToast } from '@/utils/showToast';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { apiPath } from '../apiPath';
-import { CHUNK_SIZE } from './overlays.constants';
+import {
+  CHUNK_SIZE,
+  OVERLAYS_FETCHING_ERROR_PREFIX,
+  USER_OVERLAYS_FETCHING_ERROR_PREFIX,
+  USER_OVERLAY_ADD_ERROR_PREFIX,
+  USER_OVERLAY_ADD_SUCCESS_MESSAGE,
+  USER_OVERLAY_REMOVE_ERROR_PREFIX,
+  USER_OVERLAY_REMOVE_SUCCESS_MESSAGE,
+  USER_OVERLAY_UPDATE_ERROR_PREFIX,
+  USER_OVERLAY_UPDATE_SUCCESS_MESSAGE,
+} from './overlays.constants';
 import { closeModal } from '../modal/modal.slice';
 import type { RootState } from '../store';
 
-export const getAllPublicOverlaysByProjectId = createAsyncThunk(
+export const getAllPublicOverlaysByProjectId = createAsyncThunk<MapOverlay[], string, ThunkConfig>(
   'overlays/getAllPublicOverlaysByProjectId',
-  async (projectId: string): Promise<MapOverlay[]> => {
-    const response = await axiosInstance.get<MapOverlay[]>(
-      apiPath.getAllOverlaysByProjectIdQuery(projectId, { publicOverlay: true }),
-    );
+  async (projectId: string, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstance.get<MapOverlay[]>(
+        apiPath.getAllOverlaysByProjectIdQuery(projectId, { publicOverlay: true }),
+      );
 
-    const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapOverlay));
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapOverlay));
 
-    return isDataValid ? response.data : [];
+      return isDataValid ? response.data : [];
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: OVERLAYS_FETCHING_ERROR_PREFIX });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
-export const getAllUserOverlaysByCreator = createAsyncThunk(
+export const getAllUserOverlaysByCreator = createAsyncThunk<MapOverlay[], void, ThunkConfig>(
   'overlays/getAllUserOverlaysByCreator',
-  async (_, { getState }): Promise<MapOverlay[]> => {
-    const state = getState() as RootState;
-    const creator = state.user.login;
-    if (!creator) return [];
-
-    const response = await axiosInstance<MapOverlay[]>(
-      apiPath.getAllUserOverlaysByCreatorQuery({
-        creator,
-        publicOverlay: false,
-      }),
-      {
-        withCredentials: true,
-      },
-    );
+  async (_, { getState, rejectWithValue }) => {
+    try {
+      const state = getState() as RootState;
+      const creator = state.user.login;
+      if (!creator) return [];
+
+      const response = await axiosInstance<MapOverlay[]>(
+        apiPath.getAllUserOverlaysByCreatorQuery({
+          creator,
+          publicOverlay: false,
+        }),
+        {
+          withCredentials: true,
+        },
+      );
 
-    const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapOverlay));
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(mapOverlay));
 
-    const sortByOrder = (userOverlayA: MapOverlay, userOverlayB: MapOverlay): number => {
-      if (userOverlayA.order > userOverlayB.order) return 1;
-      return -1;
-    };
+      const sortByOrder = (userOverlayA: MapOverlay, userOverlayB: MapOverlay): number => {
+        if (userOverlayA.order > userOverlayB.order) return 1;
+        return -1;
+      };
 
-    const sortedUserOverlays = response.data.sort(sortByOrder);
+      const sortedUserOverlays = response.data.sort(sortByOrder);
 
-    return isDataValid ? sortedUserOverlays : [];
+      return isDataValid ? sortedUserOverlays : [];
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: USER_OVERLAYS_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
@@ -168,68 +192,93 @@ type AddOverlayArgs = {
   projectId: string;
 };
 
-export const addOverlay = createAsyncThunk(
+export const addOverlay = createAsyncThunk<undefined, AddOverlayArgs, ThunkConfig>(
   'overlays/addOverlay',
   async (
-    { filename, content, description, name, type, projectId }: AddOverlayArgs,
-    { dispatch },
-  ): Promise<void> => {
-    const createdFile = await createFile({
-      filename,
-      content,
-    });
-
-    await uploadContent({
-      createdFile,
-      overlayContent: content,
-    });
-
-    await creteOverlay({
-      createdFile,
-      description,
-      name,
-      type,
-      projectId,
-    });
-
-    dispatch(getAllUserOverlaysByCreator());
+    { filename, content, description, name, type, projectId },
+    { rejectWithValue, dispatch },
+    // eslint-disable-next-line consistent-return
+  ) => {
+    try {
+      const createdFile = await createFile({
+        filename,
+        content,
+      });
+
+      await uploadContent({
+        createdFile,
+        overlayContent: content,
+      });
+
+      await creteOverlay({
+        createdFile,
+        description,
+        name,
+        type,
+        projectId,
+      });
+
+      await dispatch(getAllUserOverlaysByCreator());
+
+      showToast({ type: 'success', message: USER_OVERLAY_ADD_SUCCESS_MESSAGE });
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: USER_OVERLAY_ADD_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
-export const updateOverlays = createAsyncThunk(
+export const updateOverlays = createAsyncThunk<undefined, MapOverlay[], ThunkConfig>(
   'overlays/updateOverlays',
-  async (userOverlays: MapOverlay[]): Promise<void> => {
-    const userOverlaysPromises = userOverlays.map(userOverlay =>
-      axiosInstance.patch<MapOverlay>(
-        apiPath.updateOverlay(userOverlay.idObject),
-        {
-          overlay: userOverlay,
-        },
-        {
-          withCredentials: true,
-        },
-      ),
-    );
-
-    const userOverlaysResponses = await Promise.all(userOverlaysPromises);
-
-    const updatedUserOverlays = userOverlaysResponses.map(
-      updatedUserOverlay => updatedUserOverlay.data,
-    );
-
-    validateDataUsingZodSchema(updatedUserOverlays, z.array(mapOverlay));
+  // eslint-disable-next-line consistent-return
+  async (userOverlays, { rejectWithValue }) => {
+    try {
+      const userOverlaysPromises = userOverlays.map(userOverlay =>
+        axiosInstance.patch<MapOverlay>(
+          apiPath.updateOverlay(userOverlay.idObject),
+          {
+            overlay: userOverlay,
+          },
+          {
+            withCredentials: true,
+          },
+        ),
+      );
+
+      const userOverlaysResponses = await Promise.all(userOverlaysPromises);
+
+      const updatedUserOverlays = userOverlaysResponses.map(
+        updatedUserOverlay => updatedUserOverlay.data,
+      );
+
+      validateDataUsingZodSchema(updatedUserOverlays, z.array(mapOverlay));
+
+      showToast({ type: 'success', message: USER_OVERLAY_UPDATE_SUCCESS_MESSAGE });
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: USER_OVERLAY_UPDATE_ERROR_PREFIX });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
-export const removeOverlay = createAsyncThunk(
+export const removeOverlay = createAsyncThunk<undefined, { overlayId: number }, ThunkConfig>(
   'overlays/removeOverlay',
-  async ({ overlayId }: { overlayId: number }, thunkApi): Promise<void> => {
-    await axiosInstance.delete(apiPath.removeOverlay(overlayId), {
-      withCredentials: true,
-    });
+  // eslint-disable-next-line consistent-return
+  async ({ overlayId }, { dispatch, rejectWithValue }) => {
+    try {
+      await axiosInstance.delete(apiPath.removeOverlay(overlayId), {
+        withCredentials: true,
+      });
+
+      PluginsEventBus.dispatchEvent('onRemoveDataOverlay', overlayId);
+      await dispatch(getAllUserOverlaysByCreator());
+      dispatch(closeModal());
 
-    PluginsEventBus.dispatchEvent('onRemoveDataOverlay', overlayId);
-    await thunkApi.dispatch(getAllUserOverlaysByCreator());
-    thunkApi.dispatch(closeModal());
+      showToast({ type: 'success', message: USER_OVERLAY_REMOVE_SUCCESS_MESSAGE });
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: USER_OVERLAY_REMOVE_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/plugins/plugins.constants.ts b/src/redux/plugins/plugins.constants.ts
index cdd616c5..812e8503 100644
--- a/src/redux/plugins/plugins.constants.ts
+++ b/src/redux/plugins/plugins.constants.ts
@@ -15,3 +15,7 @@ export const PLUGINS_INITIAL_STATE: PluginsState = {
     currentPluginHash: undefined,
   },
 };
+
+export const PLUGIN_REGISTER_ERROR_PREFIX = 'Failed to register plugin';
+export const PLUGIN_INIT_FETCHING_ERROR_PREFIX = 'Failed to initialize fetching plugin';
+export const PLUGIN_FETCHING_ALL_ERROR_PREFIX = 'Failed to fetch all plugins';
diff --git a/src/redux/plugins/plugins.reducers.test.ts b/src/redux/plugins/plugins.reducers.test.ts
index edc59097..885f4212 100644
--- a/src/redux/plugins/plugins.reducers.test.ts
+++ b/src/redux/plugins/plugins.reducers.test.ts
@@ -69,7 +69,9 @@ describe('plugins reducer', () => {
     );
 
     expect(type).toBe('plugins/registerPlugin/rejected');
-    expect(payload).toEqual(undefined);
+    expect(payload).toEqual(
+      "Failed to register plugin: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     const { data, pluginsId } = store.getState().plugins.activePlugins;
 
     expect(data).toEqual({});
diff --git a/src/redux/plugins/plugins.thunks.ts b/src/redux/plugins/plugins.thunks.ts
index afe657f9..1d69a6cb 100644
--- a/src/redux/plugins/plugins.thunks.ts
+++ b/src/redux/plugins/plugins.thunks.ts
@@ -5,7 +5,14 @@ import type { MinervaPlugin } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import axios from 'axios';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { apiPath } from '../apiPath';
+import {
+  PLUGIN_FETCHING_ALL_ERROR_PREFIX,
+  PLUGIN_INIT_FETCHING_ERROR_PREFIX,
+  PLUGIN_REGISTER_ERROR_PREFIX,
+} from './plugins.constants';
 
 type RegisterPlugin = {
   hash: string;
@@ -15,38 +22,41 @@ type RegisterPlugin = {
   isPublic: boolean;
 };
 
-export const registerPlugin = createAsyncThunk(
+export const registerPlugin = createAsyncThunk<
+  MinervaPlugin | undefined,
+  RegisterPlugin,
+  ThunkConfig
+>(
   'plugins/registerPlugin',
-  async ({
-    hash,
-    isPublic,
-    pluginName,
-    pluginUrl,
-    pluginVersion,
-  }: RegisterPlugin): Promise<MinervaPlugin | undefined> => {
-    const payload = {
-      hash,
-      url: pluginUrl,
-      name: pluginName,
-      version: pluginVersion,
-      isPublic: isPublic.toString(),
-    } as const;
+  async ({ hash, isPublic, pluginName, pluginUrl, pluginVersion }, { rejectWithValue }) => {
+    try {
+      const payload = {
+        hash,
+        url: pluginUrl,
+        name: pluginName,
+        version: pluginVersion,
+        isPublic: isPublic.toString(),
+      } as const;
 
-    const response = await axiosInstance.post<MinervaPlugin>(
-      apiPath.registerPluign(),
-      new URLSearchParams(payload),
-      {
-        withCredentials: true,
-      },
-    );
+      const response = await axiosInstance.post<MinervaPlugin>(
+        apiPath.registerPluign(),
+        new URLSearchParams(payload),
+        {
+          withCredentials: true,
+        },
+      );
 
-    const isDataValid = validateDataUsingZodSchema(response.data, pluginSchema);
+      const isDataValid = validateDataUsingZodSchema(response.data, pluginSchema);
 
-    if (isDataValid) {
-      return response.data;
-    }
+      if (isDataValid) {
+        return response.data;
+      }
 
-    return undefined;
+      return undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: PLUGIN_REGISTER_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
@@ -61,38 +71,49 @@ type GetInitPluginsProps = {
   }) => void;
 };
 
-export const getInitPlugins = createAsyncThunk<void, GetInitPluginsProps>(
+export const getInitPlugins = createAsyncThunk<void, GetInitPluginsProps, ThunkConfig>(
   'plugins/getInitPlugins',
-  async ({ pluginsId, setHashedPlugin }): Promise<void> => {
-    /* eslint-disable no-restricted-syntax, no-await-in-loop */
-    for (const pluginId of pluginsId) {
-      const res = await axiosInstance<MinervaPlugin>(apiPath.getPlugin(pluginId));
+  // eslint-disable-next-line consistent-return
+  async ({ pluginsId, setHashedPlugin }, { rejectWithValue }) => {
+    try {
+      /* eslint-disable no-restricted-syntax, no-await-in-loop */
+      for (const pluginId of pluginsId) {
+        const res = await axiosInstance<MinervaPlugin>(apiPath.getPlugin(pluginId));
 
-      const isDataValid = validateDataUsingZodSchema(res.data, pluginSchema);
+        const isDataValid = validateDataUsingZodSchema(res.data, pluginSchema);
 
-      if (isDataValid) {
-        const { urls } = res.data;
-        const scriptRes = await axios(urls[0]);
-        const pluginScript = scriptRes.data;
-        setHashedPlugin({ pluginUrl: urls[0], pluginScript });
+        if (isDataValid) {
+          const { urls } = res.data;
+          const scriptRes = await axios(urls[0]);
+          const pluginScript = scriptRes.data;
+          setHashedPlugin({ pluginUrl: urls[0], pluginScript });
 
-        /* eslint-disable no-new-func */
-        const loadPlugin = new Function(pluginScript);
-        loadPlugin();
+          /* eslint-disable no-new-func */
+          const loadPlugin = new Function(pluginScript);
+          loadPlugin();
+        }
       }
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: PLUGIN_INIT_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
     }
   },
 );
 
-export const getAllPlugins = createAsyncThunk(
+export const getAllPlugins = createAsyncThunk<MinervaPlugin[], void, ThunkConfig>(
   'plugins/getAllPlugins',
-  async (): Promise<MinervaPlugin[]> => {
-    const response = await axiosInstance.get<MinervaPlugin[]>(apiPath.getAllPlugins());
+  async (_, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstance.get<MinervaPlugin[]>(apiPath.getAllPlugins());
 
-    const isPluginDataValid = (pluginData: MinervaPlugin): boolean =>
-      validateDataUsingZodSchema(pluginData, pluginSchema);
-    const validPlugins = response.data.filter(isPluginDataValid);
+      const isPluginDataValid = (pluginData: MinervaPlugin): boolean =>
+        validateDataUsingZodSchema(pluginData, pluginSchema);
+      const validPlugins = response.data.filter(isPluginDataValid);
 
-    return validPlugins;
+      return validPlugins;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: PLUGIN_FETCHING_ALL_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/project/project.constants.ts b/src/redux/project/project.constants.ts
new file mode 100644
index 00000000..46757eb5
--- /dev/null
+++ b/src/redux/project/project.constants.ts
@@ -0,0 +1 @@
+export const PROJECT_FETCHING_ERROR_PREFIX = 'Failed to fetch project by id';
diff --git a/src/redux/project/project.reducers.test.ts b/src/redux/project/project.reducers.test.ts
index 744f7252..f3c00776 100644
--- a/src/redux/project/project.reducers.test.ts
+++ b/src/redux/project/project.reducers.test.ts
@@ -47,10 +47,13 @@ describe('project reducer', () => {
   it('should update store after failed getProjectById query', async () => {
     mockedAxiosClient.onGet(apiPath.getProjectById(PROJECT_ID)).reply(HttpStatusCode.NotFound, []);
 
-    const { type } = await store.dispatch(getProjectById(PROJECT_ID));
+    const { type, payload } = await store.dispatch(getProjectById(PROJECT_ID));
     const { data, loading, error } = store.getState().project;
 
     expect(type).toBe('project/getProjectById/rejected');
+    expect(payload).toBe(
+      "Failed to fetch project by id: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
     expect(loading).toEqual('failed');
     expect(error).toEqual({ message: '', name: '' });
     expect(data).toEqual(undefined);
diff --git a/src/redux/project/project.thunks.ts b/src/redux/project/project.thunks.ts
index 649f867d..2ef8de82 100644
--- a/src/redux/project/project.thunks.ts
+++ b/src/redux/project/project.thunks.ts
@@ -3,15 +3,23 @@ import { axiosInstanceNewAPI } from '@/services/api/utils/axiosInstance';
 import { Project } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { apiPath } from '../apiPath';
+import { PROJECT_FETCHING_ERROR_PREFIX } from './project.constants';
 
-export const getProjectById = createAsyncThunk(
+export const getProjectById = createAsyncThunk<Project | undefined, string, ThunkConfig>(
   'project/getProjectById',
-  async (id: string): Promise<Project | undefined> => {
-    const response = await axiosInstanceNewAPI.get<Project>(apiPath.getProjectById(id));
+  async (id, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstanceNewAPI.get<Project>(apiPath.getProjectById(id));
 
-    const isDataValid = validateDataUsingZodSchema(response.data, projectSchema);
+      const isDataValid = validateDataUsingZodSchema(response.data, projectSchema);
 
-    return isDataValid ? response.data : undefined;
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: PROJECT_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/publications/publications.constatns.ts b/src/redux/publications/publications.constatns.ts
new file mode 100644
index 00000000..2f5ea0a3
--- /dev/null
+++ b/src/redux/publications/publications.constatns.ts
@@ -0,0 +1 @@
+export const PUBLICATIONS_FETCHING_ERROR_PREFIX = 'Problem with fetching publications';
diff --git a/src/redux/publications/publications.thunks.ts b/src/redux/publications/publications.thunks.ts
index e95a1d06..f50b611e 100644
--- a/src/redux/publications/publications.thunks.ts
+++ b/src/redux/publications/publications.thunks.ts
@@ -3,16 +3,25 @@ import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { axiosInstance } from '@/services/api/utils/axiosInstance';
 import { PublicationsResponse } from '@/types/models';
 import { publicationsResponseSchema } from '@/models/publicationsResponseSchema';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { GetPublicationsParams } from './publications.types';
 import { apiPath } from '../apiPath';
+import { PUBLICATIONS_FETCHING_ERROR_PREFIX } from './publications.constatns';
 
-export const getPublications = createAsyncThunk(
-  'publications/getPublications',
-  async (params: GetPublicationsParams): Promise<PublicationsResponse | undefined> => {
+export const getPublications = createAsyncThunk<
+  PublicationsResponse | undefined,
+  GetPublicationsParams,
+  ThunkConfig
+>('publications/getPublications', async (params, { rejectWithValue }) => {
+  try {
     const response = await axiosInstance.get<PublicationsResponse>(apiPath.getPublications(params));
 
     const isDataValid = validateDataUsingZodSchema(response.data, publicationsResponseSchema);
 
     return isDataValid ? response.data : undefined;
-  },
-);
+  } catch (error) {
+    const errorMessage = getErrorMessage({ error, prefix: PUBLICATIONS_FETCHING_ERROR_PREFIX });
+    return rejectWithValue(errorMessage);
+  }
+});
diff --git a/src/redux/reactions/reactions.constants.ts b/src/redux/reactions/reactions.constants.ts
index a7b9f099..c5f51f9d 100644
--- a/src/redux/reactions/reactions.constants.ts
+++ b/src/redux/reactions/reactions.constants.ts
@@ -5,3 +5,5 @@ export const REACTIONS_INITIAL_STATE: ReactionsState = {
   loading: 'idle',
   error: { name: '', message: '' },
 };
+
+export const REACTIONS_FETCHING_ERROR_PREFIX = 'Failed to fetch reactions';
diff --git a/src/redux/reactions/reactions.thunks.ts b/src/redux/reactions/reactions.thunks.ts
index dbbf7fc7..fde44e5d 100644
--- a/src/redux/reactions/reactions.thunks.ts
+++ b/src/redux/reactions/reactions.thunks.ts
@@ -2,20 +2,28 @@ import { reactionSchema } from '@/models/reaction';
 import { apiPath } from '@/redux/apiPath';
 import { axiosInstance } from '@/services/api/utils/axiosInstance';
 import { Reaction } from '@/types/models';
+import { getErrorMessage } from '@/utils/getErrorMessage';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { z } from 'zod';
+import { ThunkConfig } from '@/types/store';
+import { REACTIONS_FETCHING_ERROR_PREFIX } from './reactions.constants';
 
-export const getReactionsByIds = createAsyncThunk<Reaction[] | undefined, number[]>(
+export const getReactionsByIds = createAsyncThunk<Reaction[] | undefined, number[], ThunkConfig>(
   'reactions/getByIds',
-  async (ids: number[]): Promise<Reaction[] | undefined> => {
-    const response = await axiosInstance.get<Reaction[]>(apiPath.getReactionsWithIds(ids));
-    const isDataValid = validateDataUsingZodSchema(response.data, z.array(reactionSchema));
+  async (ids, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstance.get<Reaction[]>(apiPath.getReactionsWithIds(ids));
+      const isDataValid = validateDataUsingZodSchema(response.data, z.array(reactionSchema));
 
-    if (!isDataValid) {
-      return undefined;
-    }
+      if (!isDataValid) {
+        return undefined;
+      }
 
-    return response.data;
+      return response.data;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: REACTIONS_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/search/search.constants.ts b/src/redux/search/search.constants.ts
index 96277846..5c13f6fd 100644
--- a/src/redux/search/search.constants.ts
+++ b/src/redux/search/search.constants.ts
@@ -5,3 +5,5 @@ export const SEARCH_INITIAL_STATE: SearchState = {
   perfectMatch: false,
   loading: 'idle',
 };
+
+export const DATA_SEARCHING_ERROR_PREFIX = 'Failed to search data';
diff --git a/src/redux/search/search.thunks.ts b/src/redux/search/search.thunks.ts
index 05debcd0..078947d3 100644
--- a/src/redux/search/search.thunks.ts
+++ b/src/redux/search/search.thunks.ts
@@ -3,20 +3,34 @@ import { getMultiChemicals } from '@/redux/chemicals/chemicals.thunks';
 import { getMultiDrugs } from '@/redux/drugs/drugs.thunks';
 import { PerfectMultiSearchParams } from '@/types/search';
 import { createAsyncThunk } from '@reduxjs/toolkit';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import type { RootState } from '../store';
 import { dispatchPluginsEvents } from './search.thunks.utils';
+import { DATA_SEARCHING_ERROR_PREFIX } from './search.constants';
 
 type GetSearchDataProps = PerfectMultiSearchParams;
 
-export const getSearchData = createAsyncThunk<void, GetSearchDataProps, { state: RootState }>(
+export const getSearchData = createAsyncThunk<
+  void,
+  GetSearchDataProps,
+  { state: RootState } & ThunkConfig
+>(
   'project/getSearchData',
-  async ({ searchQueries, isPerfectMatch }, { dispatch, getState }): Promise<void> => {
-    await Promise.all([
-      dispatch(getMultiBioEntity({ searchQueries, isPerfectMatch })),
-      dispatch(getMultiDrugs(searchQueries)),
-      dispatch(getMultiChemicals(searchQueries)),
-    ]);
+  // eslint-disable-next-line consistent-return
+  async ({ searchQueries, isPerfectMatch }, { dispatch, getState, rejectWithValue }) => {
+    try {
+      await Promise.all([
+        dispatch(getMultiBioEntity({ searchQueries, isPerfectMatch })),
+        dispatch(getMultiDrugs(searchQueries)),
+        dispatch(getMultiChemicals(searchQueries)),
+      ]);
 
-    dispatchPluginsEvents(searchQueries, getState());
+      dispatchPluginsEvents(searchQueries, getState());
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: DATA_SEARCHING_ERROR_PREFIX });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/statistics/statistics.constants.ts b/src/redux/statistics/statistics.constants.ts
new file mode 100644
index 00000000..8b28b57b
--- /dev/null
+++ b/src/redux/statistics/statistics.constants.ts
@@ -0,0 +1 @@
+export const STATISTICS_FETCHING_ERROR_PREFIX = 'Failed to fetch statistics';
diff --git a/src/redux/statistics/statistics.reducers.test.ts b/src/redux/statistics/statistics.reducers.test.ts
index af16b53b..6d620db1 100644
--- a/src/redux/statistics/statistics.reducers.test.ts
+++ b/src/redux/statistics/statistics.reducers.test.ts
@@ -56,10 +56,13 @@ describe('statistics reducer', () => {
       .onGet(apiPath.getStatisticsById(PROJECT_ID))
       .reply(HttpStatusCode.NotFound, undefined);
 
-    const { type } = await store.dispatch(getStatisticsById(PROJECT_ID));
+    const { type, payload } = await store.dispatch(getStatisticsById(PROJECT_ID));
     const { loading } = store.getState().statistics;
 
     expect(type).toBe('statistics/getStatisticsById/rejected');
+    expect(payload).toBe(
+      "Failed to fetch statistics: The page you're looking for doesn't exist. Please verify the URL and try again.",
+    );
 
     waitFor(() => {
       expect(loading).toEqual('pending');
diff --git a/src/redux/statistics/statistics.thunks.ts b/src/redux/statistics/statistics.thunks.ts
index df5b6589..1a683a4e 100644
--- a/src/redux/statistics/statistics.thunks.ts
+++ b/src/redux/statistics/statistics.thunks.ts
@@ -3,15 +3,23 @@ import { Statistics } from '@/types/models';
 import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { createAsyncThunk } from '@reduxjs/toolkit';
 import { statisticsSchema } from '@/models/statisticsSchema';
+import { getErrorMessage } from '@/utils/getErrorMessage';
+import { ThunkConfig } from '@/types/store';
 import { apiPath } from '../apiPath';
+import { STATISTICS_FETCHING_ERROR_PREFIX } from './statistics.constants';
 
-export const getStatisticsById = createAsyncThunk(
+export const getStatisticsById = createAsyncThunk<Statistics | undefined, string, ThunkConfig>(
   'statistics/getStatisticsById',
-  async (id: string): Promise<Statistics | undefined> => {
-    const response = await axiosInstance.get<Statistics>(apiPath.getStatisticsById(id));
+  async (id, { rejectWithValue }) => {
+    try {
+      const response = await axiosInstance.get<Statistics>(apiPath.getStatisticsById(id));
 
-    const isDataValid = validateDataUsingZodSchema(response.data, statisticsSchema);
+      const isDataValid = validateDataUsingZodSchema(response.data, statisticsSchema);
 
-    return isDataValid ? response.data : undefined;
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({ error, prefix: STATISTICS_FETCHING_ERROR_PREFIX });
+      return rejectWithValue(errorMessage);
+    }
   },
 );
diff --git a/src/redux/store.ts b/src/redux/store.ts
index 2391ea22..3fb15d8b 100644
--- a/src/redux/store.ts
+++ b/src/redux/store.ts
@@ -29,6 +29,7 @@ import { mapListenerMiddleware } from './map/middleware/map.middleware';
 import markersReducer from './markers/markers.slice';
 import pluginsReducer from './plugins/plugins.slice';
 import publicationsReducer from './publications/publications.slice';
+import { errorListenerMiddleware } from './middlewares/error.middleware';
 import statisticsReducer from './statistics/statistics.slice';
 
 export const reducers = {
@@ -58,7 +59,7 @@ export const reducers = {
   markers: markersReducer,
 };
 
-export const middlewares = [mapListenerMiddleware.middleware];
+export const middlewares = [mapListenerMiddleware.middleware, errorListenerMiddleware.middleware];
 
 export const store = configureStore({
   reducer: reducers,
diff --git a/src/redux/user/user.thunks.ts b/src/redux/user/user.thunks.ts
index 95c567c2..e6241bb6 100644
--- a/src/redux/user/user.thunks.ts
+++ b/src/redux/user/user.thunks.ts
@@ -4,21 +4,31 @@ import { validateDataUsingZodSchema } from '@/utils/validateDataUsingZodSchema';
 import { loginSchema } from '@/models/loginSchema';
 import { sessionSchemaValid } from '@/models/sessionValidSchema';
 import { Login, SessionValid } from '@/types/models';
+import { getErrorMessage } from '@/utils/getErrorMessage';
 import { apiPath } from '../apiPath';
 import { closeModal } from '../modal/modal.slice';
 
 export const login = createAsyncThunk(
   'user/login',
-  async (credentials: { login: string; password: string }, { dispatch }) => {
-    const searchParams = new URLSearchParams(credentials);
-    const response = await axiosInstance.post<Login>(apiPath.postLogin(), searchParams, {
-      withCredentials: true,
-    });
+  async (credentials: { login: string; password: string }, { dispatch, rejectWithValue }) => {
+    try {
+      const searchParams = new URLSearchParams(credentials);
+      const response = await axiosInstance.post<Login>(apiPath.postLogin(), searchParams, {
+        withCredentials: true,
+      });
 
-    const isDataValid = validateDataUsingZodSchema(response.data, loginSchema);
-    dispatch(closeModal());
+      const isDataValid = validateDataUsingZodSchema(response.data, loginSchema);
+      dispatch(closeModal());
 
-    return isDataValid ? response.data : undefined;
+      return isDataValid ? response.data : undefined;
+    } catch (error) {
+      const errorMessage = getErrorMessage({
+        error,
+        prefix: 'Login',
+      });
+
+      return rejectWithValue(errorMessage);
+    }
   },
 );
 
diff --git a/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts b/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts
index 4f48c0a7..e29c50f9 100644
--- a/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts
+++ b/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts
@@ -19,9 +19,6 @@ export const searchByQuery = (
       if (hasFitBounds) {
         searchFitBounds();
       }
-    })
-    .catch(() => {
-      // TODO to discuss manage state of failure
     });
 
   displaySearchDrawerWithSelectedDefaultTab(searchValues);
diff --git a/src/shared/Toast/Toast.component.test.tsx b/src/shared/Toast/Toast.component.test.tsx
new file mode 100644
index 00000000..610df96f
--- /dev/null
+++ b/src/shared/Toast/Toast.component.test.tsx
@@ -0,0 +1,29 @@
+/* eslint-disable no-magic-numbers */
+import { render, screen, fireEvent } from '@testing-library/react';
+import { Toast } from './Toast.component';
+
+describe('Toast component', () => {
+  it('renders success message correctly', () => {
+    const message = 'Success message';
+    render(<Toast type="success" message={message} onDismiss={() => {}} />);
+    const toastElement = screen.getByText(message);
+    expect(toastElement).toBeInTheDocument();
+    expect(toastElement).toHaveClass('text-green-500');
+  });
+
+  it('renders error message correctly', () => {
+    const message = 'Error message';
+    render(<Toast type="error" message={message} onDismiss={() => {}} />);
+    const toastElement = screen.getByText(message);
+    expect(toastElement).toBeInTheDocument();
+    expect(toastElement).toHaveClass('text-red-500');
+  });
+
+  it('calls onDismiss when close button is clicked', () => {
+    const mockOnDismiss = jest.fn();
+    render(<Toast type="success" message="Success message" onDismiss={mockOnDismiss} />);
+    const closeButton = screen.getByRole('button');
+    fireEvent.click(closeButton);
+    expect(mockOnDismiss).toHaveBeenCalledTimes(1);
+  });
+});
diff --git a/src/shared/Toast/Toast.component.tsx b/src/shared/Toast/Toast.component.tsx
new file mode 100644
index 00000000..02e1d965
--- /dev/null
+++ b/src/shared/Toast/Toast.component.tsx
@@ -0,0 +1,30 @@
+import { twMerge } from 'tailwind-merge';
+import { Icon } from '../Icon';
+
+type ToastArgs = {
+  type: 'success' | 'error';
+  message: string;
+  onDismiss: () => void;
+};
+
+export const Toast = ({ type, message, onDismiss }: ToastArgs): React.ReactNode => (
+  <div
+    className={twMerge(
+      'flex h-[76px] w-[700px] items-center rounded-l rounded-r-lg bg-white p-4 drop-shadow before:absolute before:inset-y-0 before:left-0 before:block before:w-1 before:rounded-l-lg before:content-[""]',
+      type === 'error' ? 'before:bg-red-500' : 'before:bg-green-500',
+    )}
+  >
+    <p
+      className={twMerge(
+        'text-base font-bold ',
+        type === 'error' ? 'text-red-500' : 'text-green-500',
+      )}
+    >
+      {message}
+    </p>
+
+    <button type="button" onClick={onDismiss} className="ml-auto flex-none">
+      <Icon name="close" className="ml-3 h-7 w-7 fill-font-500" />
+    </button>
+  </div>
+);
diff --git a/src/shared/Toast/index.ts b/src/shared/Toast/index.ts
new file mode 100644
index 00000000..c28ae189
--- /dev/null
+++ b/src/shared/Toast/index.ts
@@ -0,0 +1 @@
+export { Toast } from './Toast.component';
diff --git a/src/types/store.ts b/src/types/store.ts
new file mode 100644
index 00000000..c513f8db
--- /dev/null
+++ b/src/types/store.ts
@@ -0,0 +1,3 @@
+export type ThunkConfig = {
+  rejectValue: string;
+};
diff --git a/src/utils/getErrorMessage/getErrorMessage.constants.ts b/src/utils/getErrorMessage/getErrorMessage.constants.ts
new file mode 100644
index 00000000..00b84d70
--- /dev/null
+++ b/src/utils/getErrorMessage/getErrorMessage.constants.ts
@@ -0,0 +1,11 @@
+export const UNKNOWN_ERROR = 'An unknown error occurred. Please try again later.';
+
+export const HTTP_ERROR_MESSAGES = {
+  400: "The server couldn't understand your request. Please check your input and try again.",
+  401: "You're not authorized to access this resource. Please log in or check your credentials.",
+  403: "Access Forbidden! You don't have permission to access this resource.",
+  404: "The page you're looking for doesn't exist. Please verify the URL and try again.",
+  500: 'Unexpected server error. Please try again later or contact support.',
+  501: 'Sorry, this feature is not yet implemented. Please try again later.',
+  503: 'Service Unavailable! The server is currently down for maintenance. Please try again later.',
+};
diff --git a/src/utils/getErrorMessage/getErrorMessage.test.ts b/src/utils/getErrorMessage/getErrorMessage.test.ts
new file mode 100644
index 00000000..2f9f2b7c
--- /dev/null
+++ b/src/utils/getErrorMessage/getErrorMessage.test.ts
@@ -0,0 +1,34 @@
+/* eslint-disable no-magic-numbers */
+import { getErrorMessage } from './getErrorMessage';
+import { mockAxiosError } from './getErrorMessage.test.utils';
+
+describe('getErrorMessage function', () => {
+  it('should return custom message if provided', () => {
+    const error = new Error('Custom Error');
+    const errorMessage = getErrorMessage({ error, message: 'This is a custom message' });
+    expect(errorMessage).toBe('This is a custom message');
+  });
+
+  it('should return extracted Axios error message', () => {
+    const error = mockAxiosError(401, 'Unauthorized');
+    const errorMessage = getErrorMessage({ error });
+    expect(errorMessage).toBe('Unauthorized');
+  });
+
+  it('should return error message from Error instance', () => {
+    const error = new Error('Network Error');
+    const errorMessage = getErrorMessage({ error });
+    expect(errorMessage).toBe('Network Error');
+  });
+
+  it('should return default error message if error is of unknown type', () => {
+    const errorMessage = getErrorMessage({ error: {} });
+    expect(errorMessage).toBe('An unknown error occurred. Please try again later.');
+  });
+
+  it('should prepend prefix to error message', () => {
+    const error = new Error('Server Error');
+    const errorMessage = getErrorMessage({ error, prefix: 'Error occurred' });
+    expect(errorMessage).toBe('Error occurred: Server Error');
+  });
+});
diff --git a/src/utils/getErrorMessage/getErrorMessage.test.utils.ts b/src/utils/getErrorMessage/getErrorMessage.test.utils.ts
new file mode 100644
index 00000000..cb7983e5
--- /dev/null
+++ b/src/utils/getErrorMessage/getErrorMessage.test.utils.ts
@@ -0,0 +1,15 @@
+import { AxiosError } from 'axios';
+
+type MockAxiosError = AxiosError<{ error: string; reason: string }>;
+
+export const mockAxiosError = (status: number, reason: string | null): MockAxiosError =>
+  ({
+    isAxiosError: true,
+    response: {
+      status,
+      data: {
+        reason,
+        error: reason,
+      },
+    },
+  }) as MockAxiosError;
diff --git a/src/utils/getErrorMessage/getErrorMessage.ts b/src/utils/getErrorMessage/getErrorMessage.ts
new file mode 100644
index 00000000..20073f02
--- /dev/null
+++ b/src/utils/getErrorMessage/getErrorMessage.ts
@@ -0,0 +1,29 @@
+import axios from 'axios';
+import { UNKNOWN_ERROR } from './getErrorMessage.constants';
+import { extractAxiosErrorMessage } from './getErrorMessage.utils';
+
+type GetErrorMessageConfig = {
+  error: unknown;
+  message?: string;
+  prefix?: string;
+};
+
+export const getErrorMessage = ({ error, message, prefix }: GetErrorMessageConfig): string => {
+  let errorMessage: string;
+
+  switch (true) {
+    case !!message:
+      errorMessage = message;
+      break;
+    case axios.isAxiosError(error):
+      errorMessage = extractAxiosErrorMessage(error);
+      break;
+    case error instanceof Error:
+      errorMessage = error.message;
+      break;
+    default:
+      errorMessage = UNKNOWN_ERROR;
+  }
+
+  return prefix ? `${prefix}: ${errorMessage}` : errorMessage;
+};
diff --git a/src/utils/getErrorMessage/getErrorMessage.types.ts b/src/utils/getErrorMessage/getErrorMessage.types.ts
new file mode 100644
index 00000000..33e84355
--- /dev/null
+++ b/src/utils/getErrorMessage/getErrorMessage.types.ts
@@ -0,0 +1,3 @@
+import { HTTP_ERROR_MESSAGES } from './getErrorMessage.constants';
+
+export type HttpStatuses = keyof typeof HTTP_ERROR_MESSAGES;
diff --git a/src/utils/getErrorMessage/getErrorMessage.utils.test.ts b/src/utils/getErrorMessage/getErrorMessage.utils.test.ts
new file mode 100644
index 00000000..8406dcaf
--- /dev/null
+++ b/src/utils/getErrorMessage/getErrorMessage.utils.test.ts
@@ -0,0 +1,22 @@
+/* eslint-disable no-magic-numbers */
+import { mockAxiosError } from './getErrorMessage.test.utils';
+import { extractAxiosErrorMessage } from './getErrorMessage.utils';
+
+describe('extractAxiosErrorMessage', () => {
+  it('should return the error message from Axios error response if exist', () => {
+    const error = mockAxiosError(404, 'Not Found');
+    expect(extractAxiosErrorMessage(error)).toBe('Not Found');
+  });
+  it('should return error message defined by response status if error response does not exist', () => {
+    const error = mockAxiosError(500, null);
+    expect(extractAxiosErrorMessage(error)).toBe(
+      'Unexpected server error. Please try again later or contact support.',
+    );
+  });
+  it('should return the default error message if status code is not defined in predefined error messages list and error response does not exist', () => {
+    const error = mockAxiosError(418, null);
+    expect(extractAxiosErrorMessage(error)).toBe(
+      'An unknown error occurred. Please try again later.',
+    );
+  });
+});
diff --git a/src/utils/getErrorMessage/getErrorMessage.utils.ts b/src/utils/getErrorMessage/getErrorMessage.utils.ts
new file mode 100644
index 00000000..46a46ee4
--- /dev/null
+++ b/src/utils/getErrorMessage/getErrorMessage.utils.ts
@@ -0,0 +1,14 @@
+import { AxiosError } from 'axios';
+import { HTTP_ERROR_MESSAGES, UNKNOWN_ERROR } from './getErrorMessage.constants';
+import { HttpStatuses } from './getErrorMessage.types';
+
+type Error = { error: string; reason: string };
+
+export const extractAxiosErrorMessage = (error: AxiosError<Error>): string => {
+  if (error.response?.data?.reason) {
+    return error.response.data.reason;
+  }
+
+  const status = error.response?.status as HttpStatuses;
+  return HTTP_ERROR_MESSAGES[status] || UNKNOWN_ERROR;
+};
diff --git a/src/utils/getErrorMessage/index.ts b/src/utils/getErrorMessage/index.ts
new file mode 100644
index 00000000..50ca645d
--- /dev/null
+++ b/src/utils/getErrorMessage/index.ts
@@ -0,0 +1 @@
+export { getErrorMessage } from './getErrorMessage';
diff --git a/src/utils/showToast.test.tsx b/src/utils/showToast.test.tsx
new file mode 100644
index 00000000..01f65ade
--- /dev/null
+++ b/src/utils/showToast.test.tsx
@@ -0,0 +1,21 @@
+/* eslint-disable no-magic-numbers */
+import { toast } from 'sonner';
+import { showToast } from './showToast';
+
+jest.mock('sonner', () => ({
+  toast: {
+    custom: jest.fn(),
+    dismiss: jest.fn(),
+  },
+}));
+
+describe('showToast', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  it('should call toast.custom on showToast call', () => {
+    showToast({ type: 'success', message: 'Success message' });
+    expect(toast.custom).toHaveBeenCalledTimes(1);
+  });
+});
diff --git a/src/utils/showToast.tsx b/src/utils/showToast.tsx
new file mode 100644
index 00000000..c7ea4329
--- /dev/null
+++ b/src/utils/showToast.tsx
@@ -0,0 +1,13 @@
+import { toast } from 'sonner';
+import { Toast } from '@/shared/Toast';
+
+type ShowToastArgs = {
+  type: 'success' | 'error';
+  message: string;
+};
+
+export const showToast = (args: ShowToastArgs): void => {
+  toast.custom(t => (
+    <Toast message={args.message} onDismiss={() => toast.dismiss(t)} type={args.type} />
+  ));
+};
-- 
GitLab