diff --git a/web/.eslintrc.cjs b/web/.eslintrc.cjs
index ef17242c8e..2b89e5dc7d 100644
--- a/web/.eslintrc.cjs
+++ b/web/.eslintrc.cjs
@@ -13,6 +13,7 @@ module.exports = {
     sourceType: 'module',
     ecmaVersion: 2022,
     extraFileExtensions: ['.svelte'],
+    project: ['./tsconfig.json'],
   },
   env: {
     browser: true,
@@ -32,13 +33,6 @@ module.exports = {
     NodeJS: true,
   },
   rules: {
-    'unicorn/no-useless-undefined': 'off',
-    'unicorn/prefer-spread': 'off',
-    'unicorn/no-null': 'off',
-    'unicorn/prevent-abbreviations': 'off',
-    'unicorn/no-nested-ternary': 'off',
-    'unicorn/consistent-function-scoping': 'off',
-    'unicorn/prefer-top-level-await': 'off',
     '@typescript-eslint/no-unused-vars': [
       'warn',
       {
@@ -48,5 +42,17 @@ module.exports = {
       },
     ],
     curly: 2,
+    'unicorn/no-useless-undefined': 'off',
+    'unicorn/prefer-spread': 'off',
+    'unicorn/no-null': 'off',
+    'unicorn/prevent-abbreviations': 'off',
+    'unicorn/no-nested-ternary': 'off',
+    'unicorn/consistent-function-scoping': 'off',
+    'unicorn/prefer-top-level-await': 'off',
+    // TODO: set recommended-type-checked and remove these rules
+    '@typescript-eslint/await-thenable': 'error',
+    '@typescript-eslint/no-floating-promises': 'error',
+    '@typescript-eslint/no-misused-promises': 'error',
+    '@typescript-eslint/require-await': 'error',
   },
 };
diff --git a/web/package-lock.json b/web/package-lock.json
index 78e5caf7c5..bfbd6b18f9 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -32,7 +32,7 @@
         "@socket.io/component-emitter": "^3.1.0",
         "@sveltejs/adapter-static": "^3.0.1",
         "@sveltejs/enhanced-img": "^0.1.8",
-        "@sveltejs/kit": "^2.5.1",
+        "@sveltejs/kit": "^2.5.2",
         "@sveltejs/vite-plugin-svelte": "^3.0.2",
         "@testing-library/jest-dom": "^6.1.5",
         "@testing-library/svelte": "^4.0.3",
@@ -1859,9 +1859,9 @@
       }
     },
     "node_modules/@sveltejs/kit": {
-      "version": "2.5.1",
-      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.1.tgz",
-      "integrity": "sha512-TKj08o3mJCoQNLTdRdGkHPePTCPUGTgkew65RDqjVU3MtPVxljsofXQYfXndHfq0P7KoPRO/0/reF6HesU0Djw==",
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.2.tgz",
+      "integrity": "sha512-1Pm2lsBYURQsjnLyZa+jw75eVD4gYHxGRwPyFe4DAmB3FjTVR8vRNWGeuDLGFcKMh/B1ij6FTUrc9GrerogCng==",
       "dev": true,
       "hasInstallScript": true,
       "dependencies": {
diff --git a/web/package.json b/web/package.json
index 2b53d06451..e91f2c68d7 100644
--- a/web/package.json
+++ b/web/package.json
@@ -27,7 +27,7 @@
     "@socket.io/component-emitter": "^3.1.0",
     "@sveltejs/adapter-static": "^3.0.1",
     "@sveltejs/enhanced-img": "^0.1.8",
-    "@sveltejs/kit": "^2.5.1",
+    "@sveltejs/kit": "^2.5.2",
     "@sveltejs/vite-plugin-svelte": "^3.0.2",
     "@testing-library/jest-dom": "^6.1.5",
     "@testing-library/svelte": "^4.0.3",
diff --git a/web/src/hooks.client.ts b/web/src/hooks.client.ts
index 802e9a7122..f30e3ee8ca 100644
--- a/web/src/hooks.client.ts
+++ b/web/src/hooks.client.ts
@@ -1,7 +1,6 @@
 import { isHttpError } from '@immich/sdk';
 import type { HandleClientError } from '@sveltejs/kit';
 
-const LOG_PREFIX = '[hooks.client.ts]';
 const DEFAULT_MESSAGE = 'Hmm, not sure about that. Check the logs or open a ticket?';
 
 const parseError = (error: unknown) => {
@@ -23,6 +22,6 @@ const parseError = (error: unknown) => {
 
 export const handleError: HandleClientError = ({ error }) => {
   const result = parseError(error);
-  console.error(`${LOG_PREFIX}:handleError ${result.message}`);
+  console.error(`[hooks.client.ts]:handleError ${result.message}`);
   return result;
 };
diff --git a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
index 60756166f2..edf5c48300 100644
--- a/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
+++ b/web/src/lib/components/admin-page/jobs/jobs-panel.svelte
@@ -48,11 +48,11 @@
     await handleCommand(jobId, dto);
   };
 
-  const onConfirm = () => {
+  const onConfirm = async () => {
     if (!confirmJob) {
       return;
     }
-    handleCommand(confirmJob, { command: JobCommand.Start, force: true });
+    await handleCommand(confirmJob, { command: JobCommand.Start, force: true });
     confirmJob = null;
   };
 
diff --git a/web/src/lib/components/admin-page/settings/admin-settings.svelte b/web/src/lib/components/admin-page/settings/admin-settings.svelte
index 1ad962b0de..16b2afc7fe 100644
--- a/web/src/lib/components/admin-page/settings/admin-settings.svelte
+++ b/web/src/lib/components/admin-page/settings/admin-settings.svelte
@@ -54,7 +54,7 @@
     });
   };
 
-  const resetToDefault = async (configKeys: Array<keyof SystemConfigDto>) => {
+  const resetToDefault = (configKeys: Array<keyof SystemConfigDto>) => {
     for (const key of configKeys) {
       config = { ...config, [key]: defaultConfig[key] };
     }
diff --git a/web/src/lib/components/album-page/album-viewer.svelte b/web/src/lib/components/album-page/album-viewer.svelte
index e9983cc7ec..d4f60ab60a 100644
--- a/web/src/lib/components/album-page/album-viewer.svelte
+++ b/web/src/lib/components/album-page/album-viewer.svelte
@@ -21,6 +21,7 @@
   import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
   import { mdiFileImagePlusOutline, mdiFolderDownloadOutline } from '@mdi/js';
   import UpdatePanel from '../shared-components/update-panel.svelte';
+  import { handlePromiseError } from '$lib/utils';
 
   export let sharedLink: SharedLinkResponseDto;
   export let user: UserResponseDto | undefined = undefined;
@@ -35,7 +36,7 @@
 
   dragAndDropFilesStore.subscribe((value) => {
     if (value.isDragging && value.files.length > 0) {
-      fileUploadHandler(value.files, album.id);
+      handlePromiseError(fileUploadHandler(value.files, album.id));
       dragAndDropFilesStore.set({ isDragging: false, files: [] });
     }
   });
@@ -67,7 +68,7 @@
 
   const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
 
-  onMount(async () => {
+  onMount(() => {
     document.addEventListener('keydown', onKeyboardPress);
   });
 
diff --git a/web/src/lib/components/asset-viewer/activity-viewer.svelte b/web/src/lib/components/asset-viewer/activity-viewer.svelte
index dff2d7f319..4dd1b75e79 100644
--- a/web/src/lib/components/asset-viewer/activity-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/activity-viewer.svelte
@@ -1,7 +1,7 @@
 <script lang="ts">
   import Icon from '$lib/components/elements/icon.svelte';
   import { timeBeforeShowLoadingSpinner } from '$lib/constants';
-  import { getAssetThumbnailUrl } from '$lib/utils';
+  import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
   import { getAssetType } from '$lib/utils/asset-utils';
   import { autoGrowHeight } from '$lib/utils/autogrow';
   import { clickOutside } from '$lib/utils/click-outside';
@@ -79,7 +79,7 @@
 
   $: {
     if (assetId && previousAssetId != assetId) {
-      getReactions();
+      handlePromiseError(getReactions());
       previousAssetId = assetId;
     }
   }
@@ -95,10 +95,10 @@
     }
   };
 
-  const handleEnter = (event: KeyboardEvent) => {
+  const handleEnter = async (event: KeyboardEvent) => {
     if (event.key === 'Enter') {
       event.preventDefault();
-      handleSendComment();
+      await handleSendComment();
       return;
     }
   };
diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte
index 2c52cd4a3a..3869c60970 100644
--- a/web/src/lib/components/asset-viewer/asset-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte
@@ -10,7 +10,7 @@
   import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
   import { stackAssetsStore } from '$lib/stores/stacked-asset.store';
   import { user } from '$lib/stores/user.store';
-  import { getAssetJobMessage, isSharedLink } from '$lib/utils';
+  import { getAssetJobMessage, isSharedLink, handlePromiseError } from '$lib/utils';
   import { addAssetsToAlbum, downloadFile } from '$lib/utils/asset-utils';
   import { handleError } from '$lib/utils/handle-error';
   import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
@@ -174,8 +174,8 @@
 
   $: {
     if (isShared && asset.id) {
-      getFavorite();
-      getNumberOfComments();
+      handlePromiseError(getFavorite());
+      handlePromiseError(getNumberOfComments());
     }
   }
 
@@ -184,9 +184,9 @@
       if (value === SlideshowState.PlaySlideshow) {
         slideshowHistory.reset();
         slideshowHistory.queue(asset.id);
-        handlePlaySlideshow();
+        handlePromiseError(handlePlaySlideshow());
       } else if (value === SlideshowState.StopSlideshow) {
-        handleStopSlideshow();
+        handlePromiseError(handleStopSlideshow());
       }
     });
 
@@ -226,7 +226,7 @@
     }
   });
 
-  $: asset.id && !sharedLink && handleGetAllAlbums(); // Update the album information when the asset ID changes
+  $: asset.id && !sharedLink && handlePromiseError(handleGetAllAlbums()); // Update the album information when the asset ID changes
 
   const handleGetAllAlbums = async () => {
     if (isSharedLink()) {
@@ -247,7 +247,7 @@
     isShowActivity = !isShowActivity;
   };
 
-  const handleKeypress = (event: KeyboardEvent) => {
+  const handleKeypress = async (event: KeyboardEvent) => {
     if (shouldIgnoreShortcut(event)) {
       return;
     }
@@ -264,7 +264,7 @@
       case 'a':
       case 'A': {
         if (shiftKey) {
-          toggleArchive();
+          await toggleArchive();
         }
         return;
       }
@@ -273,18 +273,18 @@
         return;
       }
       case 'ArrowRight': {
-        navigateAssetForward();
+        await navigateAssetForward();
         return;
       }
       case 'd':
       case 'D': {
         if (shiftKey) {
-          downloadFile(asset);
+          await downloadFile(asset);
         }
         return;
       }
       case 'Delete': {
-        trashOrDelete(shiftKey);
+        await trashOrDelete(shiftKey);
         return;
       }
       case 'Escape': {
@@ -296,7 +296,7 @@
         return;
       }
       case 'f': {
-        toggleFavorite();
+        await toggleFavorite();
         return;
       }
       case 'i': {
@@ -326,7 +326,7 @@
 
     slideshowHistory.queue(asset.id);
 
-    setAssetId(asset.id);
+    await setAssetId(asset.id);
     $restartSlideshowProgress = true;
   };
 
@@ -369,17 +369,17 @@
     $isShowDetail = !$isShowDetail;
   };
 
-  const trashOrDelete = (force: boolean = false) => {
+  const trashOrDelete = async (force: boolean = false) => {
     if (force || !isTrashEnabled) {
       if ($showDeleteModal) {
         isShowDeleteConfirmation = true;
         return;
       }
-      deleteAsset();
+      await deleteAsset();
       return;
     }
 
-    trashAsset();
+    await trashAsset();
     return;
   };
 
@@ -432,7 +432,7 @@
         message: asset.isFavorite ? `Added to favorites` : `Removed from favorites`,
       });
     } catch (error) {
-      await handleError(error, `Unable to ${asset.isFavorite ? `add asset to` : `remove asset from`} favorites`);
+      handleError(error, `Unable to ${asset.isFavorite ? `add asset to` : `remove asset from`} favorites`);
     }
   };
 
@@ -472,7 +472,7 @@
         message: asset.isArchived ? `Added to archive` : `Removed from archive`,
       });
     } catch (error) {
-      await handleError(error, `Unable to ${asset.isArchived ? `add asset to` : `remove asset from`} archive`);
+      handleError(error, `Unable to ${asset.isArchived ? `add asset to` : `remove asset from`} archive`);
     }
   };
 
@@ -481,7 +481,7 @@
       await runAssetJobs({ assetJobsDto: { assetIds: [asset.id], name } });
       notificationController.show({ type: NotificationType.Info, message: getAssetJobMessage(name) });
     } catch (error) {
-      await handleError(error, `Unable to submit job`);
+      handleError(error, `Unable to submit job`);
     }
   };
 
@@ -492,7 +492,7 @@
   let assetViewerHtmlElement: HTMLElement;
 
   const slideshowHistory = new SlideshowHistory((assetId: string) => {
-    setAssetId(assetId);
+    handlePromiseError(setAssetId(assetId));
     $restartSlideshowProgress = true;
   });
 
@@ -550,7 +550,7 @@
       dispatch('close');
       notificationController.show({ type: NotificationType.Info, message: 'Un-stacked', timeout: 1500 });
     } catch (error) {
-      await handleError(error, `Unable to unstack`);
+      handleError(error, `Unable to unstack`);
     }
   };
 </script>
diff --git a/web/src/lib/components/asset-viewer/detail-panel.svelte b/web/src/lib/components/asset-viewer/detail-panel.svelte
index 38f65e3df7..7f94857afc 100644
--- a/web/src/lib/components/asset-viewer/detail-panel.svelte
+++ b/web/src/lib/components/asset-viewer/detail-panel.svelte
@@ -7,7 +7,7 @@
   import { featureFlags } from '$lib/stores/server-config.store';
   import { user } from '$lib/stores/user.store';
   import { websocketEvents } from '$lib/stores/websocket';
-  import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink } from '$lib/utils';
+  import { getAssetThumbnailUrl, getPeopleThumbnailUrl, isSharedLink, handlePromiseError } from '$lib/utils';
   import { delay, getAssetFilename } from '$lib/utils/asset-utils';
   import { autoGrowHeight } from '$lib/utils/autogrow';
   import { clickOutside } from '$lib/utils/click-outside';
@@ -78,7 +78,7 @@
     originalDescription = description;
   };
 
-  $: handleNewAsset(asset);
+  $: handlePromiseError(handleNewAsset(asset));
 
   $: latlng = (() => {
     const lat = asset.exifInfo?.latitude;
@@ -113,7 +113,7 @@
     switch (event.key) {
       case 'Enter': {
         if (ctrl && event.target === textArea) {
-          handleFocusOut();
+          await handleFocusOut();
         }
       }
     }
diff --git a/web/src/lib/components/asset-viewer/photo-viewer.svelte b/web/src/lib/components/asset-viewer/photo-viewer.svelte
index e10d5573ca..79e8276153 100644
--- a/web/src/lib/components/asset-viewer/photo-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/photo-viewer.svelte
@@ -4,7 +4,7 @@
   import { boundingBoxesArray } from '$lib/stores/people.store';
   import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
   import { photoZoomState } from '$lib/stores/zoom-image.store';
-  import { getKey } from '$lib/utils';
+  import { getKey, handlePromiseError } from '$lib/utils';
   import { isWebCompatibleImage } from '$lib/utils/asset-utils';
   import { getBoundingBox } from '$lib/utils/people-utils';
   import { shouldIgnoreShortcut } from '$lib/utils/shortcut';
@@ -102,7 +102,7 @@
     }
   };
 
-  const doZoomImage = async () => {
+  const doZoomImage = () => {
     setZoomImageWheelState({
       currentZoom: $zoomImageWheelState.currentZoom === 1 ? 2 : 1,
     });
@@ -120,7 +120,7 @@
     if (state.currentZoom > 1 && isWebCompatibleImage(asset) && !hasZoomed && !$alwaysLoadOriginalFile) {
       hasZoomed = true;
 
-      loadAssetData({ loadOriginal: true });
+      handlePromiseError(loadAssetData({ loadOriginal: true }));
     }
   });
 </script>
diff --git a/web/src/lib/components/asset-viewer/video-viewer.svelte b/web/src/lib/components/asset-viewer/video-viewer.svelte
index 96713d6217..f5ff5f0fc2 100644
--- a/web/src/lib/components/asset-viewer/video-viewer.svelte
+++ b/web/src/lib/components/asset-viewer/video-viewer.svelte
@@ -20,7 +20,7 @@
       video.muted = false;
       dispatch('onVideoStarted');
     } catch (error) {
-      await handleError(error, 'Unable to play video');
+      handleError(error, 'Unable to play video');
     } finally {
       isVideoLoading = false;
     }
diff --git a/web/src/lib/components/faces-page/assign-face-side-panel.svelte b/web/src/lib/components/faces-page/assign-face-side-panel.svelte
index 6b8d8af174..ce7212f950 100644
--- a/web/src/lib/components/faces-page/assign-face-side-panel.svelte
+++ b/web/src/lib/components/faces-page/assign-face-side-panel.svelte
@@ -49,7 +49,7 @@
     if (assetType === AssetTypeEnum.Image) {
       image = $photoViewer;
     } else if (assetType === AssetTypeEnum.Video) {
-      const data = await getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp);
+      const data = getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp);
       const img: HTMLImageElement = new Image();
       img.src = data;
 
diff --git a/web/src/lib/components/faces-page/merge-face-selector.svelte b/web/src/lib/components/faces-page/merge-face-selector.svelte
index 434bfb88ae..7b075405ca 100644
--- a/web/src/lib/components/faces-page/merge-face-selector.svelte
+++ b/web/src/lib/components/faces-page/merge-face-selector.svelte
@@ -43,10 +43,10 @@
     dispatch('back');
   };
 
-  const handleSwapPeople = () => {
+  const handleSwapPeople = async () => {
     [person, selectedPeople[0]] = [selectedPeople[0], person];
     $page.url.searchParams.set(QueryParameter.ACTION, ActionQueryParameterValue.MERGE);
-    goto(`${AppRoute.PEOPLE}/${person.id}?${$page.url.searchParams.toString()}`);
+    await goto(`${AppRoute.PEOPLE}/${person.id}?${$page.url.searchParams.toString()}`);
   };
 
   const onSelect = (selected: PersonResponseDto) => {
diff --git a/web/src/lib/components/faces-page/person-side-panel.svelte b/web/src/lib/components/faces-page/person-side-panel.svelte
index 91a9587998..087301ea1e 100644
--- a/web/src/lib/components/faces-page/person-side-panel.svelte
+++ b/web/src/lib/components/faces-page/person-side-panel.svelte
@@ -3,7 +3,7 @@
   import { timeBeforeShowLoadingSpinner } from '$lib/constants';
   import { boundingBoxesArray } from '$lib/stores/people.store';
   import { websocketEvents } from '$lib/stores/websocket';
-  import { getPeopleThumbnailUrl } from '$lib/utils';
+  import { getPeopleThumbnailUrl, handlePromiseError } from '$lib/utils';
   import { handleError } from '$lib/utils/handle-error';
   import { getPersonNameWithHiddenValue } from '$lib/utils/person';
   import {
@@ -85,7 +85,7 @@
   };
 
   onMount(() => {
-    loadPeople();
+    handlePromiseError(loadPeople());
     return websocketEvents.on('on_person_thumbnail', onPersonThumbnail);
   });
 
@@ -164,7 +164,7 @@
     }
   };
 
-  const handlePersonPicker = async (index: number) => {
+  const handlePersonPicker = (index: number) => {
     editedPersonIndex = index;
     showSeletecFaces = true;
   };
diff --git a/web/src/lib/components/faces-page/unmerge-face-selector.svelte b/web/src/lib/components/faces-page/unmerge-face-selector.svelte
index 3c09b208d5..254594988d 100644
--- a/web/src/lib/components/faces-page/unmerge-face-selector.svelte
+++ b/web/src/lib/components/faces-page/unmerge-face-selector.svelte
@@ -132,9 +132,7 @@
           title={'Assign selected assets to a new person'}
           size={'sm'}
           disabled={disableButtons || hasSelection}
-          on:click={() => {
-            handleCreate();
-          }}
+          on:click={handleCreate}
         >
           {#if !showLoadingSpinnerCreate}
             <Icon path={mdiPlus} size={18} />
@@ -147,9 +145,7 @@
           size={'sm'}
           title={'Assign selected assets to an existing person'}
           disabled={disableButtons || !hasSelection}
-          on:click={() => {
-            handleReassign();
-          }}
+          on:click={handleReassign}
         >
           {#if !showLoadingSpinnerReassign}
             <div>
diff --git a/web/src/lib/components/forms/library-scan-settings-form.svelte b/web/src/lib/components/forms/library-scan-settings-form.svelte
index f110a5a091..4d6c9c0dd5 100644
--- a/web/src/lib/components/forms/library-scan-settings-form.svelte
+++ b/web/src/lib/components/forms/library-scan-settings-form.svelte
@@ -37,7 +37,7 @@
     dispatch('submit', { library, type: LibraryType.External });
   };
 
-  const handleAddExclusionPattern = async () => {
+  const handleAddExclusionPattern = () => {
     if (!addExclusionPattern) {
       return;
     }
@@ -60,7 +60,7 @@
     }
   };
 
-  const handleEditExclusionPattern = async () => {
+  const handleEditExclusionPattern = () => {
     if (editExclusionPattern === null) {
       return;
     }
@@ -79,7 +79,7 @@
     }
   };
 
-  const handleDeleteExclusionPattern = async () => {
+  const handleDeleteExclusionPattern = () => {
     if (editExclusionPattern === null) {
       return;
     }
diff --git a/web/src/lib/components/forms/login-form.svelte b/web/src/lib/components/forms/login-form.svelte
index 9ea654a80c..202c6b60c3 100644
--- a/web/src/lib/components/forms/login-form.svelte
+++ b/web/src/lib/components/forms/login-form.svelte
@@ -47,7 +47,7 @@
         return;
       }
     } catch (error) {
-      await handleError(error, 'Unable to connect!');
+      handleError(error, 'Unable to connect!');
     }
 
     oauthLoading = false;
diff --git a/web/src/lib/components/memory-page/memory-viewer.svelte b/web/src/lib/components/memory-page/memory-viewer.svelte
index c07672fd1c..30617f0aa9 100644
--- a/web/src/lib/components/memory-page/memory-viewer.svelte
+++ b/web/src/lib/components/memory-page/memory-viewer.svelte
@@ -8,7 +8,7 @@
   import { AppRoute, QueryParameter } from '$lib/constants';
   import type { Viewport } from '$lib/stores/assets.store';
   import { memoryStore } from '$lib/stores/memory.store';
-  import { getAssetThumbnailUrl } from '$lib/utils';
+  import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
   import { fromLocalDateTime } from '$lib/utils/timeline-util';
   import { ThumbnailFormat, getMemoryLane } from '@immich/sdk';
   import { mdiChevronDown, mdiChevronLeft, mdiChevronRight, mdiChevronUp, mdiPause, mdiPlay } from '@mdi/js';
@@ -59,30 +59,30 @@
   let paused = false;
 
   // Play or pause progress when the paused state changes.
-  $: paused ? pause() : play();
+  $: paused ? handlePromiseError(pause()) : handlePromiseError(play());
 
   // Progress should be paused when it's no longer possible to advance.
   $: paused ||= !canGoForward || galleryInView;
 
   // Advance to the next asset or memory when progress is complete.
-  $: $progress === 1 && toNext();
+  $: $progress === 1 && handlePromiseError(toNext());
 
   // Progress should be resumed when reset and not paused.
-  $: !$progress && !paused && play();
+  $: !$progress && !paused && handlePromiseError(play());
 
   // Progress should be reset when the current memory or asset changes.
-  $: memoryIndex, assetIndex, reset();
+  $: memoryIndex, assetIndex, handlePromiseError(reset());
 
-  const handleKeyDown = (e: KeyboardEvent) => {
+  const handleKeyDown = async (e: KeyboardEvent) => {
     if (e.key === 'ArrowRight' && canGoForward) {
       e.preventDefault();
-      toNext();
+      await toNext();
     } else if (e.key === 'ArrowLeft' && canGoBack) {
       e.preventDefault();
-      toPrevious();
+      await toPrevious();
     } else if (e.key === 'Escape') {
       e.preventDefault();
-      goto(AppRoute.PHOTOS);
+      await goto(AppRoute.PHOTOS);
     }
   };
 
diff --git a/web/src/lib/components/photos-page/actions/add-to-album.svelte b/web/src/lib/components/photos-page/actions/add-to-album.svelte
index c7101922f4..cb059947bb 100644
--- a/web/src/lib/components/photos-page/actions/add-to-album.svelte
+++ b/web/src/lib/components/photos-page/actions/add-to-album.svelte
@@ -27,18 +27,22 @@
     showAlbumPicker = false;
 
     const assetIds = [...getAssets()].map((asset) => asset.id);
-    createAlbum({ createAlbumDto: { albumName, assetIds } }).then((response) => {
-      const { id, albumName } = response;
+    createAlbum({ createAlbumDto: { albumName, assetIds } })
+      .then(async (response) => {
+        const { id, albumName } = response;
 
-      notificationController.show({
-        message: `Added ${assetIds.length} to ${albumName}`,
-        type: NotificationType.Info,
+        notificationController.show({
+          message: `Added ${assetIds.length} to ${albumName}`,
+          type: NotificationType.Info,
+        });
+
+        clearSelect();
+
+        await goto(`${AppRoute.ALBUMS}/${id}`);
+      })
+      .catch((error) => {
+        console.error(`[add-to-album.svelte]:handleAddToNewAlbum ${error}`, error);
       });
-
-      clearSelect();
-
-      goto(`${AppRoute.ALBUMS}/${id}`);
-    });
   };
 
   const handleAddToAlbum = async (album: AlbumResponseDto) => {
diff --git a/web/src/lib/components/photos-page/asset-date-group.svelte b/web/src/lib/components/photos-page/asset-date-group.svelte
index d8372525b6..0c40c87108 100644
--- a/web/src/lib/components/photos-page/asset-date-group.svelte
+++ b/web/src/lib/components/photos-page/asset-date-group.svelte
@@ -80,13 +80,17 @@
     });
   }
 
-  const assetClickHandler = (asset: AssetResponseDto, assetsInDateGroup: AssetResponseDto[], groupTitle: string) => {
+  const assetClickHandler = async (
+    asset: AssetResponseDto,
+    assetsInDateGroup: AssetResponseDto[],
+    groupTitle: string,
+  ) => {
     if (isSelectionMode || $isMultiSelectState) {
       assetSelectHandler(asset, assetsInDateGroup, groupTitle);
       return;
     }
 
-    assetViewingStore.setAssetId(asset.id);
+    await assetViewingStore.setAssetId(asset.id);
   };
 
   const handleSelectGroup = (title: string, assets: AssetResponseDto[]) => dispatch('select', { title, assets });
diff --git a/web/src/lib/components/photos-page/asset-grid.svelte b/web/src/lib/components/photos-page/asset-grid.svelte
index bfb0edd7e1..a512adaad5 100644
--- a/web/src/lib/components/photos-page/asset-grid.svelte
+++ b/web/src/lib/components/photos-page/asset-grid.svelte
@@ -21,6 +21,7 @@
   import ShowShortcuts from '../shared-components/show-shortcuts.svelte';
   import AssetDateGroup from './asset-date-group.svelte';
   import DeleteAssetDialog from './delete-asset-dialog.svelte';
+  import { handlePromiseError } from '$lib/utils';
 
   export let isSelectionMode = false;
   export let singleSelect = false;
@@ -47,19 +48,19 @@
   $: isEmpty = $assetStore.initialized && $assetStore.buckets.length === 0;
   $: idsSelectedAssets = [...$selectedAssets].filter((a) => !a.isExternal).map((a) => a.id);
 
-  const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
   const dispatch = createEventDispatcher<{ select: AssetResponseDto; escape: void }>();
 
+  const onKeydown = (event: KeyboardEvent) => handlePromiseError(handleKeyboardPress(event));
   onMount(async () => {
     showSkeleton = false;
-    document.addEventListener('keydown', onKeyboardPress);
+    document.addEventListener('keydown', onKeydown);
     assetStore.connect();
     await assetStore.init(viewport);
   });
 
   onDestroy(() => {
     if (browser) {
-      document.removeEventListener('keydown', onKeyboardPress);
+      document.removeEventListener('keydown', onKeydown);
     }
 
     if ($showAssetViewer) {
@@ -69,13 +70,13 @@
     assetStore.disconnect();
   });
 
-  const trashOrDelete = (force: boolean = false) => {
+  const trashOrDelete = async (force: boolean = false) => {
     isShowDeleteConfirmation = false;
-    deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets);
+    await deleteAssets(!(isTrashEnabled && !force), (assetId) => assetStore.removeAsset(assetId), idsSelectedAssets);
     assetInteractionStore.clearMultiselect();
   };
 
-  const handleKeyboardPress = (event: KeyboardEvent) => {
+  const handleKeyboardPress = async (event: KeyboardEvent) => {
     if ($isSearchEnabled || shouldIgnoreShortcut(event)) {
       return;
     }
@@ -98,7 +99,7 @@
         }
         case '/': {
           event.preventDefault();
-          goto(AppRoute.EXPLORE);
+          await goto(AppRoute.EXPLORE);
           return;
         }
         case 'Delete': {
@@ -112,7 +113,7 @@
               force = true;
             }
 
-            trashOrDelete(force);
+            await trashOrDelete(force);
           }
           return;
         }
@@ -126,12 +127,12 @@
     }
   };
 
-  function intersectedHandler(event: CustomEvent) {
+  async function intersectedHandler(event: CustomEvent) {
     const element_ = event.detail.container as HTMLElement;
     const target = element_.firstChild as HTMLElement;
     if (target) {
       const bucketDate = target.id.split('_')[1];
-      assetStore.loadBucket(bucketDate, event.detail.position);
+      await assetStore.loadBucket(bucketDate, event.detail.position);
     }
   }
 
@@ -142,7 +143,7 @@
   const handlePrevious = async () => {
     const previousAsset = await assetStore.getPreviousAssetId($viewingAsset.id);
     if (previousAsset) {
-      assetViewingStore.setAssetId(previousAsset);
+      await assetViewingStore.setAssetId(previousAsset);
     }
 
     return !!previousAsset;
@@ -151,7 +152,7 @@
   const handleNext = async () => {
     const nextAsset = await assetStore.getNextAssetId($viewingAsset.id);
     if (nextAsset) {
-      assetViewingStore.setAssetId(nextAsset);
+      await assetViewingStore.setAssetId(nextAsset);
     }
 
     return !!nextAsset;
@@ -369,7 +370,7 @@
   <DeleteAssetDialog
     size={idsSelectedAssets.length}
     on:cancel={() => (isShowDeleteConfirmation = false)}
-    on:confirm={() => trashOrDelete(true)}
+    on:confirm={() => handlePromiseError(trashOrDelete(true))}
   />
 {/if}
 
diff --git a/web/src/lib/components/share-page/individual-shared-viewer.svelte b/web/src/lib/components/share-page/individual-shared-viewer.svelte
index 0036331378..b587f1e13b 100644
--- a/web/src/lib/components/share-page/individual-shared-viewer.svelte
+++ b/web/src/lib/components/share-page/individual-shared-viewer.svelte
@@ -2,7 +2,7 @@
   import { goto } from '$app/navigation';
   import { AppRoute } from '$lib/constants';
   import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
-  import { getKey } from '$lib/utils';
+  import { getKey, handlePromiseError } from '$lib/utils';
   import { downloadArchive } from '$lib/utils/asset-utils';
   import { fileUploadHandler, openFileUploadDialog } from '$lib/utils/file-uploader';
   import { handleError } from '$lib/utils/handle-error';
@@ -29,7 +29,7 @@
 
   dragAndDropFilesStore.subscribe((value) => {
     if (value.isDragging && value.files.length > 0) {
-      handleUploadAssets(value.files);
+      handlePromiseError(handleUploadAssets(value.files));
       dragAndDropFilesStore.set({ isDragging: false, files: [] });
     }
   });
@@ -59,7 +59,7 @@
         type: NotificationType.Info,
       });
     } catch (error) {
-      await handleError(error, 'Unable to add assets to shared link');
+      handleError(error, 'Unable to add assets to shared link');
     }
   };
 
diff --git a/web/src/lib/components/shared-components/map/map.svelte b/web/src/lib/components/shared-components/map/map.svelte
index 682042604e..063d5971cd 100644
--- a/web/src/lib/components/shared-components/map/map.svelte
+++ b/web/src/lib/components/shared-components/map/map.svelte
@@ -2,7 +2,7 @@
   import Icon from '$lib/components/elements/icon.svelte';
   import { Theme } from '$lib/constants';
   import { colorTheme, mapSettings } from '$lib/stores/preferences.store';
-  import { getAssetThumbnailUrl } from '$lib/utils';
+  import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
   import { getMapStyle, MapTheme, type MapMarkerResponseDto } from '@immich/sdk';
   import { mdiCog, mdiMapMarker } from '@mdi/js';
   import type { Feature, GeoJsonProperties, Geometry, Point } from 'geojson';
@@ -152,9 +152,7 @@
         applyToClusters
         asButton
         let:feature
-        on:click={(event) => {
-          handleClusterClick(event.detail.feature.properties.cluster_id, map);
-        }}
+        on:click={(event) => handlePromiseError(handleClusterClick(event.detail.feature.properties.cluster_id, map))}
       >
         <div
           class="rounded-full w-[40px] h-[40px] bg-immich-primary text-immich-gray flex justify-center items-center font-mono font-bold shadow-lg hover:bg-immich-dark-primary transition-all duration-200 hover:text-immich-dark-bg opacity-90"
diff --git a/web/src/lib/components/shared-components/navigation-loading-bar.svelte b/web/src/lib/components/shared-components/navigation-loading-bar.svelte
index c7c1da3c91..c1d39ece57 100644
--- a/web/src/lib/components/shared-components/navigation-loading-bar.svelte
+++ b/web/src/lib/components/shared-components/navigation-loading-bar.svelte
@@ -8,8 +8,8 @@
     easing: cubicOut,
   });
 
-  onMount(() => {
-    progress.set(90);
+  onMount(async () => {
+    await progress.set(90);
   });
 </script>
 
diff --git a/web/src/lib/components/shared-components/portal/portal.svelte b/web/src/lib/components/shared-components/portal/portal.svelte
index c3dee4212c..924e5f0c6b 100644
--- a/web/src/lib/components/shared-components/portal/portal.svelte
+++ b/web/src/lib/components/shared-components/portal/portal.svelte
@@ -1,4 +1,5 @@
 <script context="module" lang="ts">
+  import { handlePromiseError } from '$lib/utils';
   import { tick } from 'svelte';
 
   /**
@@ -36,7 +37,7 @@
       }
     }
 
-    update(target);
+    handlePromiseError(update(target));
     return {
       update,
       destroy,
diff --git a/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte b/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte
index dfc556d71f..ab247042b6 100644
--- a/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte
+++ b/web/src/lib/components/shared-components/progress-bar/progress-bar.svelte
@@ -6,6 +6,8 @@
 </script>
 
 <script lang="ts">
+  import { handlePromiseError } from '$lib/utils';
+
   import { createEventDispatcher, onMount } from 'svelte';
   import { tweened } from 'svelte/motion';
 
@@ -24,14 +26,14 @@
 
   export let duration = 5;
 
-  const onChange = () => {
+  const onChange = async () => {
     progress = setDuration(duration);
-    play();
+    await play();
   };
 
   let progress = setDuration(duration);
 
-  $: duration, onChange();
+  $: duration, handlePromiseError(onChange());
 
   $: {
     if ($progress === 1) {
@@ -45,35 +47,35 @@
     paused: void;
   }>();
 
-  onMount(() => {
+  onMount(async () => {
     if (autoplay) {
-      play();
+      await play();
     }
   });
 
-  export const play = () => {
+  export const play = async () => {
     status = ProgressBarStatus.Playing;
     dispatch('playing');
-    progress.set(1);
+    await progress.set(1);
   };
 
-  export const pause = () => {
+  export const pause = async () => {
     status = ProgressBarStatus.Paused;
     dispatch('paused');
-    progress.set($progress);
+    await progress.set($progress);
   };
 
-  export const restart = (autoplay: boolean) => {
-    progress.set(0);
+  export const restart = async (autoplay: boolean) => {
+    await progress.set(0);
 
     if (autoplay) {
-      play();
+      await play();
     }
   };
 
-  export const reset = () => {
+  export const reset = async () => {
     status = ProgressBarStatus.Paused;
-    progress.set(0);
+    await progress.set(0);
   };
 
   function setDuration(newDuration: number) {
diff --git a/web/src/lib/components/shared-components/search-bar/search-bar.svelte b/web/src/lib/components/shared-components/search-bar/search-bar.svelte
index 3ca9e35832..5d14d061e7 100644
--- a/web/src/lib/components/shared-components/search-bar/search-bar.svelte
+++ b/web/src/lib/components/shared-components/search-bar/search-bar.svelte
@@ -10,6 +10,7 @@
   import SearchFilterBox from './search-filter-box.svelte';
   import type { MetadataSearchDto, SmartSearchDto } from '@immich/sdk';
   import { getMetadataSearchQuery } from '$lib/utils/metadata-search';
+  import { handlePromiseError } from '$lib/utils';
 
   export let value = '';
   export let grayTheme: boolean;
@@ -21,13 +22,13 @@
   let showFilter = false;
   $: showClearIcon = value.length > 0;
 
-  const onSearch = (payload: SmartSearchDto | MetadataSearchDto) => {
+  const onSearch = async (payload: SmartSearchDto | MetadataSearchDto) => {
     const params = getMetadataSearchQuery(payload);
 
     showHistory = false;
     showFilter = false;
     $isSearchEnabled = false;
-    goto(`${AppRoute.SEARCH}?${params}`);
+    await goto(`${AppRoute.SEARCH}?${params}`);
   };
 
   const clearSearchTerm = (searchTerm: string) => {
@@ -63,9 +64,9 @@
     showFilter = false;
   };
 
-  const onHistoryTermClick = (searchTerm: string) => {
+  const onHistoryTermClick = async (searchTerm: string) => {
     const searchPayload = { query: searchTerm };
-    onSearch(searchPayload);
+    await onSearch(searchPayload);
   };
 
   const onFilterClick = () => {
@@ -78,7 +79,7 @@
   };
 
   const onSubmit = () => {
-    onSearch({ query: value });
+    handlePromiseError(onSearch({ query: value }));
     saveSearchTerm(value);
   };
 </script>
@@ -141,7 +142,7 @@
       <SearchHistoryBox
         on:clearAllSearchTerms={clearAllSearchTerms}
         on:clearSearchTerm={({ detail: searchTerm }) => clearSearchTerm(searchTerm)}
-        on:selectSearchTerm={({ detail: searchTerm }) => onHistoryTermClick(searchTerm)}
+        on:selectSearchTerm={({ detail: searchTerm }) => handlePromiseError(onHistoryTermClick(searchTerm))}
       />
     {/if}
   </form>
diff --git a/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte b/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte
index adc824aec0..6f377faffb 100644
--- a/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte
+++ b/web/src/lib/components/shared-components/search-bar/search-camera-section.svelte
@@ -8,6 +8,7 @@
 <script lang="ts">
   import { SearchSuggestionType, getSearchSuggestions } from '@immich/sdk';
   import Combobox, { toComboBoxOptions } from '../combobox.svelte';
+  import { handlePromiseError } from '$lib/utils';
 
   export let filters: SearchCameraFilter;
 
@@ -16,8 +17,8 @@
 
   $: makeFilter = filters.make;
   $: modelFilter = filters.model;
-  $: updateMakes(modelFilter);
-  $: updateModels(makeFilter);
+  $: handlePromiseError(updateMakes(modelFilter));
+  $: handlePromiseError(updateModels(makeFilter));
 
   async function updateMakes(model?: string) {
     makes = await getSearchSuggestions({
diff --git a/web/src/lib/components/shared-components/search-bar/search-filter-box.svelte b/web/src/lib/components/shared-components/search-bar/search-filter-box.svelte
index eb7cc0d871..8d060f4d0d 100644
--- a/web/src/lib/components/shared-components/search-bar/search-filter-box.svelte
+++ b/web/src/lib/components/shared-components/search-bar/search-filter-box.svelte
@@ -82,7 +82,7 @@
     };
   };
 
-  const search = async () => {
+  const search = () => {
     if (filter.context && filter.personIds.size > 0) {
       handleError(
         new Error('Context search does not support people filter'),
diff --git a/web/src/lib/components/shared-components/search-bar/search-location-section.svelte b/web/src/lib/components/shared-components/search-bar/search-location-section.svelte
index 2a713e9a04..ac412c5138 100644
--- a/web/src/lib/components/shared-components/search-bar/search-location-section.svelte
+++ b/web/src/lib/components/shared-components/search-bar/search-location-section.svelte
@@ -9,6 +9,7 @@
 <script lang="ts">
   import { getSearchSuggestions, SearchSuggestionType } from '@immich/sdk';
   import Combobox, { toComboBoxOptions } from '../combobox.svelte';
+  import { handlePromiseError } from '$lib/utils';
 
   export let filters: SearchLocationFilter;
 
@@ -18,9 +19,9 @@
 
   $: countryFilter = filters.country;
   $: stateFilter = filters.state;
-  $: updateCountries();
-  $: updateStates(countryFilter);
-  $: updateCities(countryFilter, stateFilter);
+  $: handlePromiseError(updateCountries());
+  $: handlePromiseError(updateStates(countryFilter));
+  $: handlePromiseError(updateCities(countryFilter, stateFilter));
 
   async function updateCountries() {
     countries = await getSearchSuggestions({
diff --git a/web/src/lib/components/shared-components/settings/setting-accordion.svelte b/web/src/lib/components/shared-components/settings/setting-accordion.svelte
index 76f29b1f6e..b6ce4b3de0 100755
--- a/web/src/lib/components/shared-components/settings/setting-accordion.svelte
+++ b/web/src/lib/components/shared-components/settings/setting-accordion.svelte
@@ -1,7 +1,7 @@
 <script lang="ts">
   import { page } from '$app/stores';
   import { QueryParameter } from '$lib/constants';
-  import { hasParamValue, updateParamList } from '$lib/utils';
+  import { hasParamValue, handlePromiseError, updateParamList } from '$lib/utils';
   import { slide } from 'svelte/transition';
 
   export let title: string;
@@ -12,12 +12,12 @@
   const syncFromUrl = () => (isOpen = hasParamValue(QueryParameter.IS_OPEN, key));
   const syncToUrl = (isOpen: boolean) => updateParamList({ param: QueryParameter.IS_OPEN, value: key, add: isOpen });
 
-  isOpen ? syncToUrl(true) : syncFromUrl();
+  isOpen ? handlePromiseError(syncToUrl(true)) : syncFromUrl();
   $: $page.url && syncFromUrl();
 
-  const toggle = () => {
+  const toggle = async () => {
     isOpen = !isOpen;
-    syncToUrl(isOpen);
+    await syncToUrl(isOpen);
   };
 </script>
 
diff --git a/web/src/lib/components/shared-components/upload-asset-preview.svelte b/web/src/lib/components/shared-components/upload-asset-preview.svelte
index 7d5a634fbf..4aa6f18ec5 100644
--- a/web/src/lib/components/shared-components/upload-asset-preview.svelte
+++ b/web/src/lib/components/shared-components/upload-asset-preview.svelte
@@ -13,9 +13,9 @@
 
   export let uploadAsset: UploadAsset;
 
-  const handleRetry = (uploadAsset: UploadAsset) => {
+  const handleRetry = async (uploadAsset: UploadAsset) => {
     uploadAssetsStore.removeUploadAsset(uploadAsset.id);
-    fileUploadHandler([uploadAsset.file], uploadAsset.albumId);
+    await fileUploadHandler([uploadAsset.file], uploadAsset.albumId);
   };
 </script>
 
diff --git a/web/src/lib/components/user-settings-page/library-list.svelte b/web/src/lib/components/user-settings-page/library-list.svelte
index c11b41332f..440becde0b 100644
--- a/web/src/lib/components/user-settings-page/library-list.svelte
+++ b/web/src/lib/components/user-settings-page/library-list.svelte
@@ -56,8 +56,8 @@
   let selectedLibraryIndex = 0;
   let selectedLibrary: LibraryResponseDto | null = null;
 
-  onMount(() => {
-    readLibraryList();
+  onMount(async () => {
+    await readLibraryList();
   });
 
   const closeAll = () => {
@@ -234,11 +234,11 @@
     updateLibraryIndex = selectedLibraryIndex;
   };
 
-  const onScanNewLibraryClicked = () => {
+  const onScanNewLibraryClicked = async () => {
     closeAll();
 
     if (selectedLibrary) {
-      handleScan(selectedLibrary.id);
+      await handleScan(selectedLibrary.id);
     }
   };
 
@@ -248,38 +248,38 @@
     updateLibraryIndex = selectedLibraryIndex;
   };
 
-  const onScanAllLibraryFilesClicked = () => {
+  const onScanAllLibraryFilesClicked = async () => {
     closeAll();
     if (selectedLibrary) {
-      handleScanChanges(selectedLibrary.id);
+      await handleScanChanges(selectedLibrary.id);
     }
   };
 
-  const onForceScanAllLibraryFilesClicked = () => {
+  const onForceScanAllLibraryFilesClicked = async () => {
     closeAll();
     if (selectedLibrary) {
-      handleForceScan(selectedLibrary.id);
+      await handleForceScan(selectedLibrary.id);
     }
   };
 
-  const onRemoveOfflineFilesClicked = () => {
+  const onRemoveOfflineFilesClicked = async () => {
     closeAll();
     if (selectedLibrary) {
-      handleRemoveOffline(selectedLibrary.id);
+      await handleRemoveOffline(selectedLibrary.id);
     }
   };
 
-  const onDeleteLibraryClicked = () => {
+  const onDeleteLibraryClicked = async () => {
     closeAll();
 
     if (selectedLibrary && confirm(`Are you sure you want to delete ${selectedLibrary.name} library?`) == true) {
-      refreshStats(selectedLibraryIndex);
+      await refreshStats(selectedLibraryIndex);
       if (totalCount[selectedLibraryIndex] > 0) {
         deleteAssetCount = totalCount[selectedLibraryIndex];
         confirmDeleteLibrary = selectedLibrary;
       } else {
         deletedLibrary = selectedLibrary;
-        handleDelete();
+        await handleDelete();
       }
     }
   };
@@ -348,27 +348,27 @@
 
                 {#if showContextMenu}
                   <Portal target="body">
-                    <ContextMenu {...contextMenuPosition} on:outclick={() => onMenuExit()}>
-                      <MenuOption on:click={() => onRenameClicked()} text={`Rename`} />
+                    <ContextMenu {...contextMenuPosition} on:outclick={onMenuExit}>
+                      <MenuOption on:click={onRenameClicked} text={`Rename`} />
 
                       {#if selectedLibrary && selectedLibrary.type === LibraryType.External}
-                        <MenuOption on:click={() => onEditImportPathClicked()} text="Edit Import Paths" />
-                        <MenuOption on:click={() => onScanSettingClicked()} text="Scan Settings" />
+                        <MenuOption on:click={onEditImportPathClicked} text="Edit Import Paths" />
+                        <MenuOption on:click={onScanSettingClicked} text="Scan Settings" />
                         <hr />
-                        <MenuOption on:click={() => onScanNewLibraryClicked()} text="Scan New Library Files" />
+                        <MenuOption on:click={onScanNewLibraryClicked} text="Scan New Library Files" />
                         <MenuOption
-                          on:click={() => onScanAllLibraryFilesClicked()}
+                          on:click={onScanAllLibraryFilesClicked}
                           text="Re-scan All Library Files"
                           subtitle={'Only refreshes modified files'}
                         />
                         <MenuOption
-                          on:click={() => onForceScanAllLibraryFilesClicked()}
+                          on:click={onForceScanAllLibraryFilesClicked}
                           text="Force Re-scan All Library Files"
                           subtitle={'Refreshes every file'}
                         />
                         <hr />
-                        <MenuOption on:click={() => onRemoveOfflineFilesClicked()} text="Remove Offline Files" />
-                        <MenuOption on:click={() => onDeleteLibraryClicked()}>
+                        <MenuOption on:click={onRemoveOfflineFilesClicked} text="Remove Offline Files" />
+                        <MenuOption on:click={onDeleteLibraryClicked}>
                           <p class="text-red-600">Delete library</p>
                         </MenuOption>
                       {/if}
diff --git a/web/src/lib/components/user-settings-page/oauth-settings.svelte b/web/src/lib/components/user-settings-page/oauth-settings.svelte
index 4ac27190bb..e17acd1678 100644
--- a/web/src/lib/components/user-settings-page/oauth-settings.svelte
+++ b/web/src/lib/components/user-settings-page/oauth-settings.svelte
@@ -28,7 +28,7 @@
       } catch (error) {
         handleError(error, 'Unable to link OAuth account');
       } finally {
-        goto('?open=oauth');
+        await goto('?open=oauth');
       }
     }
 
diff --git a/web/src/lib/components/user-settings-page/partner-settings.svelte b/web/src/lib/components/user-settings-page/partner-settings.svelte
index f29cf42961..fbe5efa58a 100644
--- a/web/src/lib/components/user-settings-page/partner-settings.svelte
+++ b/web/src/lib/components/user-settings-page/partner-settings.svelte
@@ -31,8 +31,8 @@
   let removePartnerDto: PartnerResponseDto | null = null;
   let partners: Array<PartnerSharing> = [];
 
-  onMount(() => {
-    refreshPartners();
+  onMount(async () => {
+    await refreshPartners();
   });
 
   const refreshPartners = async () => {
diff --git a/web/src/lib/stores/assets.store.ts b/web/src/lib/stores/assets.store.ts
index 4f1f481a31..940877e36a 100644
--- a/web/src/lib/stores/assets.store.ts
+++ b/web/src/lib/stores/assets.store.ts
@@ -172,15 +172,17 @@ export class AssetStore {
     this.emit(false);
 
     let height = 0;
+    const loaders = [];
     for (const bucket of this.buckets) {
       if (height < viewport.height) {
         height += bucket.bucketHeight;
-        this.loadBucket(bucket.bucketDate, BucketPosition.Visible);
+        loaders.push(this.loadBucket(bucket.bucketDate, BucketPosition.Visible));
         continue;
       }
 
       break;
     }
+    await Promise.all(loaders);
   }
 
   async loadBucket(bucketDate: string, position: BucketPosition): Promise<void> {
diff --git a/web/src/lib/stores/websocket.ts b/web/src/lib/stores/websocket.ts
index a025ef8cda..375cd3e75c 100644
--- a/web/src/lib/stores/websocket.ts
+++ b/web/src/lib/stores/websocket.ts
@@ -47,7 +47,7 @@ websocket
   .on('on_new_release', (releaseVersion) => websocketStore.release.set(releaseVersion))
   .on('connect_error', (e) => console.log('Websocket Connect Error', e));
 
-export const openWebsocketConnection = async () => {
+export const openWebsocketConnection = () => {
   try {
     if (!get(user)) {
       return;
diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts
index 644dd7f638..fa914bfcb2 100644
--- a/web/src/lib/utils.ts
+++ b/web/src/lib/utils.ts
@@ -194,3 +194,13 @@ export const findLocale = (code: string | undefined) => {
     name: language?.name,
   };
 };
+
+export const asyncTimeout = (ms: number) => {
+  return new Promise((resolve) => {
+    setTimeout(resolve, ms);
+  });
+};
+
+export const handlePromiseError = <T>(promise: Promise<T>): void => {
+  promise.catch((error) => console.error(`[utils.ts]:handlePromiseError ${error}`, error));
+};
diff --git a/web/src/lib/utils/executor-queue.spec.ts b/web/src/lib/utils/executor-queue.spec.ts
index 7533dc7686..b6ba77e9f3 100644
--- a/web/src/lib/utils/executor-queue.spec.ts
+++ b/web/src/lib/utils/executor-queue.spec.ts
@@ -28,10 +28,14 @@ describe('Executor Queue test', function () {
       });
 
     // The first 3 should be finished within 200ms (concurrency 3)
+    // eslint-disable-next-line @typescript-eslint/no-floating-promises
     eq.addTask(() => timeoutPromiseBuilder(100, 'T1'));
+    // eslint-disable-next-line @typescript-eslint/no-floating-promises
     eq.addTask(() => timeoutPromiseBuilder(200, 'T2'));
+    // eslint-disable-next-line @typescript-eslint/no-floating-promises
     eq.addTask(() => timeoutPromiseBuilder(150, 'T3'));
     // The last task will be executed after 200ms and will finish at 400ms
+    // eslint-disable-next-line @typescript-eslint/no-floating-promises
     eq.addTask(() => timeoutPromiseBuilder(200, 'T4'));
 
     expect(finished).not.toBeCalled();
diff --git a/web/src/lib/utils/executor-queue.ts b/web/src/lib/utils/executor-queue.ts
index 0744427cfc..7d3d3aa29f 100644
--- a/web/src/lib/utils/executor-queue.ts
+++ b/web/src/lib/utils/executor-queue.ts
@@ -1,3 +1,5 @@
+import { handlePromiseError } from '$lib/utils';
+
 interface Options {
   concurrency: number;
 }
@@ -66,6 +68,6 @@ export class ExecutorQueue {
       return;
     }
 
-    runnable();
+    handlePromiseError(runnable());
   }
 }
diff --git a/web/src/lib/utils/file-uploader.ts b/web/src/lib/utils/file-uploader.ts
index 929c1ee550..2f22803b6a 100644
--- a/web/src/lib/utils/file-uploader.ts
+++ b/web/src/lib/utils/file-uploader.ts
@@ -29,7 +29,7 @@ export const openFileUploadDialog = async (albumId?: string | undefined) => {
       fileSelector.type = 'file';
       fileSelector.multiple = true;
       fileSelector.accept = extensions.join(',');
-      fileSelector.addEventListener('change', async (e: Event) => {
+      fileSelector.addEventListener('change', (e: Event) => {
         const target = e.target as HTMLInputElement;
         if (!target.files) {
           return;
@@ -119,7 +119,7 @@ async function fileUploader(asset: File, albumId: string | undefined = undefined
       }
     })
     .catch(async (error) => {
-      await handleError(error, 'Unable to upload file');
+      handleError(error, 'Unable to upload file');
       const reason = (await getServerErrorMessage(error)) || error;
       uploadAssetsStore.updateAsset(deviceAssetId, { state: UploadState.ERROR, error: reason });
       return undefined;
diff --git a/web/src/lib/utils/handle-error.ts b/web/src/lib/utils/handle-error.ts
index 23727e4118..67aa3df5f4 100644
--- a/web/src/lib/utils/handle-error.ts
+++ b/web/src/lib/utils/handle-error.ts
@@ -22,20 +22,25 @@ export async function getServerErrorMessage(error: unknown) {
   }
 }
 
-export async function handleError(error: unknown, message: string) {
+export function handleError(error: unknown, message: string) {
   if ((error as Error)?.name === 'AbortError') {
     return;
   }
 
   console.error(`[handleError]: ${message}`, error, (error as Error)?.stack);
 
-  let serverMessage = await getServerErrorMessage(error);
-  if (serverMessage) {
-    serverMessage = `${String(serverMessage).slice(0, 75)}\n(Immich Server Error)`;
-  }
+  getServerErrorMessage(error)
+    .then((serverMessage) => {
+      if (serverMessage) {
+        serverMessage = `${String(serverMessage).slice(0, 75)}\n(Immich Server Error)`;
+      }
 
-  notificationController.show({
-    message: serverMessage || message,
-    type: NotificationType.Error,
-  });
+      notificationController.show({
+        message: serverMessage || message,
+        type: NotificationType.Error,
+      });
+    })
+    .catch((error) => {
+      console.error(error);
+    });
 }
diff --git a/web/src/routes/(user)/albums/+page.svelte b/web/src/routes/(user)/albums/+page.svelte
index 07000d9136..4f1cccbb0b 100644
--- a/web/src/routes/(user)/albums/+page.svelte
+++ b/web/src/routes/(user)/albums/+page.svelte
@@ -196,7 +196,7 @@
   const handleCreateAlbum = async () => {
     const newAlbum = await createAlbum();
     if (newAlbum) {
-      goto(`${AppRoute.ALBUMS}/${newAlbum.id}`);
+      await goto(`${AppRoute.ALBUMS}/${newAlbum.id}`);
     }
   };
 
@@ -204,8 +204,8 @@
     return new Date(dateString).toLocaleDateString($locale, dateFormats.album);
   };
 
-  onMount(() => {
-    removeAlbumsIfEmpty();
+  onMount(async () => {
+    await removeAlbumsIfEmpty();
   });
 
   const removeAlbumsIfEmpty = async () => {
diff --git a/web/src/routes/(user)/albums/[albumId]/+page.svelte b/web/src/routes/(user)/albums/[albumId]/+page.svelte
index 7c9ca39acd..e511e67ed0 100644
--- a/web/src/routes/(user)/albums/[albumId]/+page.svelte
+++ b/web/src/routes/(user)/albums/[albumId]/+page.svelte
@@ -220,12 +220,11 @@
 
   onMount(async () => {
     if (album.sharedUsers.length > 0) {
-      getFavorite();
-      getNumberOfComments();
+      await Promise.all([getFavorite(), getNumberOfComments()]);
     }
   });
 
-  const handleKeypress = async (event: KeyboardEvent) => {
+  const handleKeypress = (event: KeyboardEvent) => {
     if (event.target !== textArea) {
       return;
     }
@@ -242,12 +241,12 @@
   const handleStartSlideshow = async () => {
     const asset = $slideshowShuffle ? await assetStore.getRandomAsset() : assetStore.assets[0];
     if (asset) {
-      setAssetId(asset.id);
+      await setAssetId(asset.id);
       $slideshowState = SlideshowState.PlaySlideshow;
     }
   };
 
-  const handleEscape = () => {
+  const handleEscape = async () => {
     if (viewMode === ViewMode.SELECT_USERS) {
       viewMode = ViewMode.VIEW;
       return;
@@ -275,7 +274,7 @@
       assetInteractionStore.clearMultiselect();
       return;
     }
-    goto(backUrl);
+    await goto(backUrl);
     return;
   };
 
@@ -371,7 +370,7 @@
 
   const handleRemoveUser = async (userId: string) => {
     if (userId == 'me' || userId === $user.id) {
-      goto(backUrl);
+      await goto(backUrl);
       return;
     }
 
@@ -390,7 +389,7 @@
   const handleRemoveAlbum = async () => {
     try {
       await deleteAlbum({ id: album.id });
-      goto(backUrl);
+      await goto(backUrl);
     } catch (error) {
       handleError(error, 'Unable to delete album');
     } finally {
diff --git a/web/src/routes/(user)/albums/[albumId]/photos/[assetId]/+page.ts b/web/src/routes/(user)/albums/[albumId]/photos/[assetId]/+page.ts
index 687d74ea18..5c0ff2677d 100644
--- a/web/src/routes/(user)/albums/[albumId]/photos/[assetId]/+page.ts
+++ b/web/src/routes/(user)/albums/[albumId]/photos/[assetId]/+page.ts
@@ -2,7 +2,7 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load: PageLoad = async ({ params }) => {
+export const load: PageLoad = ({ params }) => {
   const albumId = params.albumId;
 
   if (albumId) {
diff --git a/web/src/routes/(user)/albums/albums.bloc.ts b/web/src/routes/(user)/albums/albums.bloc.ts
index f608787fab..89a933e0f0 100644
--- a/web/src/routes/(user)/albums/albums.bloc.ts
+++ b/web/src/routes/(user)/albums/albums.bloc.ts
@@ -1,5 +1,6 @@
 import type { OnShowContextMenuDetail } from '$lib/components/album-page/album-card';
 import { notificationController, NotificationType } from '$lib/components/shared-components/notification/notification';
+import { asyncTimeout } from '$lib/utils';
 import { handleError } from '$lib/utils/handle-error';
 import { createAlbum, deleteAlbum, getAllAlbums, type AlbumResponseDto } from '@immich/sdk';
 import { derived, get, writable } from 'svelte/store';
@@ -20,9 +21,8 @@ export const useAlbums = (properties: AlbumsProperties) => {
       // Delete album that has no photos and is named ''
       for (const album of data) {
         if (album.albumName === '' && album.assetCount === 0) {
-          setTimeout(async () => {
-            await handleDeleteAlbum(album);
-          }, 500);
+          await asyncTimeout(500);
+          await handleDeleteAlbum(album);
         }
       }
     } catch {
@@ -46,10 +46,7 @@ export const useAlbums = (properties: AlbumsProperties) => {
     albums.set(get(albums).filter(({ id }) => id !== albumToDelete.id));
   }
 
-  async function showAlbumContextMenu(
-    contextMenuDetail: OnShowContextMenuDetail,
-    album: AlbumResponseDto,
-  ): Promise<void> {
+  function showAlbumContextMenu(contextMenuDetail: OnShowContextMenuDetail, album: AlbumResponseDto): void {
     contextMenuTargetAlbum.set(album);
 
     contextMenuPosition.set({
diff --git a/web/src/routes/(user)/archive/photos/[assetId]/+page.ts b/web/src/routes/(user)/archive/photos/[assetId]/+page.ts
index 83c2975ca3..6a39c40daf 100644
--- a/web/src/routes/(user)/archive/photos/[assetId]/+page.ts
+++ b/web/src/routes/(user)/archive/photos/[assetId]/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load: PageLoad = async () => {
+export const load: PageLoad = () => {
   redirect(302, AppRoute.ARCHIVE);
 };
diff --git a/web/src/routes/(user)/favorites/[assetId]/+page.ts b/web/src/routes/(user)/favorites/[assetId]/+page.ts
index b5d469c2a6..5125ef1e45 100644
--- a/web/src/routes/(user)/favorites/[assetId]/+page.ts
+++ b/web/src/routes/(user)/favorites/[assetId]/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load: PageLoad = async () => {
+export const load: PageLoad = () => {
   redirect(302, AppRoute.FAVORITES);
 };
diff --git a/web/src/routes/(user)/map/+page.svelte b/web/src/routes/(user)/map/+page.svelte
index a129b9fed9..257c0ad89d 100644
--- a/web/src/routes/(user)/map/+page.svelte
+++ b/web/src/routes/(user)/map/+page.svelte
@@ -15,6 +15,7 @@
   import { DateTime, Duration } from 'luxon';
   import { onDestroy, onMount } from 'svelte';
   import type { PageData } from './$types';
+  import { handlePromiseError } from '$lib/utils';
 
   export let data: PageData;
 
@@ -26,8 +27,8 @@
   let viewingAssetCursor = 0;
   let showSettingsModal = false;
 
-  onMount(() => {
-    loadMapMarkers().then((data) => (mapMarkers = data));
+  onMount(async () => {
+    mapMarkers = await loadMapMarkers();
   });
 
   onDestroy(() => {
@@ -35,7 +36,7 @@
     assetViewingStore.showAssetViewer(false);
   });
 
-  $: $featureFlags.map || goto(AppRoute.PHOTOS);
+  $: $featureFlags.map || handlePromiseError(goto(AppRoute.PHOTOS));
   const omit = (obj: MapSettings, key: string) => {
     return Object.fromEntries(Object.entries(obj).filter(([k]) => k !== key));
   };
@@ -85,21 +86,21 @@
     }
   }
 
-  function onViewAssets(assetIds: string[]) {
-    assetViewingStore.setAssetId(assetIds[0]);
+  async function onViewAssets(assetIds: string[]) {
+    await assetViewingStore.setAssetId(assetIds[0]);
     viewingAssets = assetIds;
     viewingAssetCursor = 0;
   }
 
-  function navigateNext() {
+  async function navigateNext() {
     if (viewingAssetCursor < viewingAssets.length - 1) {
-      assetViewingStore.setAssetId(viewingAssets[++viewingAssetCursor]);
+      await assetViewingStore.setAssetId(viewingAssets[++viewingAssetCursor]);
     }
   }
 
-  function navigatePrevious() {
+  async function navigatePrevious() {
     if (viewingAssetCursor > 0) {
-      assetViewingStore.setAssetId(viewingAssets[--viewingAssetCursor]);
+      await assetViewingStore.setAssetId(viewingAssets[--viewingAssetCursor]);
     }
   }
 </script>
diff --git a/web/src/routes/(user)/memory/photos/[assetId]/+page.ts b/web/src/routes/(user)/memory/photos/[assetId]/+page.ts
index 428a3caae9..f90b109917 100644
--- a/web/src/routes/(user)/memory/photos/[assetId]/+page.ts
+++ b/web/src/routes/(user)/memory/photos/[assetId]/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load = (async () => {
+export const load = (() => {
   redirect(302, AppRoute.PHOTOS);
 }) satisfies PageLoad;
diff --git a/web/src/routes/(user)/people/+page.svelte b/web/src/routes/(user)/people/+page.svelte
index 1372d254b9..6373b81fd5 100644
--- a/web/src/routes/(user)/people/+page.svelte
+++ b/web/src/routes/(user)/people/+page.svelte
@@ -81,12 +81,12 @@
 
   const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
 
-  onMount(() => {
+  onMount(async () => {
     document.addEventListener('keydown', onKeyboardPress);
     const getSearchedPeople = $page.url.searchParams.get(QueryParameter.SEARCHED_PEOPLE);
     if (getSearchedPeople) {
       searchName = getSearchedPeople;
-      handleSearchPeople(true);
+      await handleSearchPeople(true);
     }
   });
 
@@ -108,10 +108,10 @@
     }
   };
 
-  const handleSearch = (force: boolean) => {
+  const handleSearch = async (force: boolean) => {
     $page.url.searchParams.set(QueryParameter.SEARCHED_PEOPLE, searchName);
-    goto($page.url);
-    handleSearchPeople(force);
+    await goto($page.url);
+    await handleSearchPeople(force);
   };
 
   const handleCloseClick = () => {
@@ -293,8 +293,8 @@
     }
   };
 
-  const handleMergePeople = (detail: PersonResponseDto) => {
-    goto(
+  const handleMergePeople = async (detail: PersonResponseDto) => {
+    await goto(
       `${AppRoute.PEOPLE}/${detail.id}?${QueryParameter.ACTION}=${ActionQueryParameterValue.MERGE}&${QueryParameter.PREVIOUS_ROUTE}=${AppRoute.PEOPLE}`,
     );
   };
@@ -303,7 +303,7 @@
     if (searchName === '') {
       if ($page.url.searchParams.has(QueryParameter.SEARCHED_PEOPLE)) {
         $page.url.searchParams.delete(QueryParameter.SEARCHED_PEOPLE);
-        goto($page.url);
+        await goto($page.url);
       }
       return;
     }
@@ -331,7 +331,7 @@
       return;
     }
     if (personName === '') {
-      changeName();
+      await changeName();
       return;
     }
     const data = await searchPerson({ name: personName, withHidden: true });
@@ -359,7 +359,7 @@
         .slice(0, 3);
       return;
     }
-    changeName();
+    await changeName();
   };
 
   const submitBirthDateChange = async (value: string) => {
diff --git a/web/src/routes/(user)/people/[personId]/+page.svelte b/web/src/routes/(user)/people/[personId]/+page.svelte
index f572ff2cb7..dd4f2d7bd1 100644
--- a/web/src/routes/(user)/people/[personId]/+page.svelte
+++ b/web/src/routes/(user)/people/[personId]/+page.svelte
@@ -185,7 +185,7 @@
     }
   };
 
-  const handleEscape = () => {
+  const handleEscape = async () => {
     if ($showAssetViewer || viewMode === ViewMode.SUGGEST_MERGE) {
       return;
     }
@@ -193,7 +193,7 @@
       assetInteractionStore.clearMultiselect();
       return;
     } else {
-      goto(previousRoute);
+      await goto(previousRoute);
       return;
     }
   };
@@ -235,7 +235,7 @@
         type: NotificationType.Info,
       });
 
-      goto(previousRoute, { replaceState: true });
+      await goto(previousRoute, { replaceState: true });
     } catch (error) {
       handleError(error, 'Unable to hide person');
     }
@@ -244,7 +244,7 @@
   const handleMerge = async (person: PersonResponseDto) => {
     const { assets } = await getPersonStatistics({ id: person.id });
     numberOfAssets = assets;
-    handleGoBack();
+    await handleGoBack();
 
     data.person = person;
 
@@ -292,7 +292,7 @@
         refreshAssetGrid = !refreshAssetGrid;
         return;
       }
-      goto(`${AppRoute.PEOPLE}/${personToBeMergedIn.id}`, { replaceState: true });
+      await goto(`${AppRoute.PEOPLE}/${personToBeMergedIn.id}`, { replaceState: true });
     } catch (error) {
       handleError(error, 'Unable to save name');
     }
@@ -341,7 +341,7 @@
       return;
     }
     if (name === '') {
-      changeName();
+      await changeName();
       return;
     }
 
@@ -366,7 +366,7 @@
       viewMode = ViewMode.SUGGEST_MERGE;
       return;
     }
-    changeName();
+    await changeName();
   };
 
   const handleSetBirthDate = async (birthDate: string) => {
@@ -392,11 +392,11 @@
     }
   };
 
-  const handleGoBack = () => {
+  const handleGoBack = async () => {
     viewMode = ViewMode.VIEW_ASSETS;
     if ($page.url.searchParams.has(QueryParameter.ACTION)) {
       $page.url.searchParams.delete(QueryParameter.ACTION);
-      goto($page.url);
+      await goto($page.url);
     }
   };
 </script>
diff --git a/web/src/routes/(user)/people/[personId]/photos/[assetId]/+page.ts b/web/src/routes/(user)/people/[personId]/photos/[assetId]/+page.ts
index 5ac7adf5c9..bb2e587da4 100644
--- a/web/src/routes/(user)/people/[personId]/photos/[assetId]/+page.ts
+++ b/web/src/routes/(user)/people/[personId]/photos/[assetId]/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load = (async ({ params }) => {
+export const load = (({ params }) => {
   redirect(302, `${AppRoute.PEOPLE}/${params.personId}`);
 }) satisfies PageLoad;
diff --git a/web/src/routes/(user)/photos/[assetId]/+page.ts b/web/src/routes/(user)/photos/[assetId]/+page.ts
index 428a3caae9..f90b109917 100644
--- a/web/src/routes/(user)/photos/[assetId]/+page.ts
+++ b/web/src/routes/(user)/photos/[assetId]/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load = (async () => {
+export const load = (() => {
   redirect(302, AppRoute.PHOTOS);
 }) satisfies PageLoad;
diff --git a/web/src/routes/(user)/search/+page.svelte b/web/src/routes/(user)/search/+page.svelte
index bda74db788..28ad6bcbd7 100644
--- a/web/src/routes/(user)/search/+page.svelte
+++ b/web/src/routes/(user)/search/+page.svelte
@@ -35,6 +35,7 @@
   import type { Viewport } from '$lib/stores/assets.store';
   import { locale } from '$lib/stores/preferences.store';
   import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
+  import { handlePromiseError } from '$lib/utils';
   import { parseUtcDate } from '$lib/utils/date-time';
 
   const MAX_ASSET_COUNT = 5000;
@@ -53,7 +54,7 @@
 
   const onKeyboardPress = (event: KeyboardEvent) => handleKeyboardPress(event);
 
-  const handleKeyboardPress = (event: KeyboardEvent) => {
+  const handleKeyboardPress = async (event: KeyboardEvent) => {
     if (shouldIgnoreShortcut(event)) {
       return;
     }
@@ -65,7 +66,7 @@
             return;
           }
           if (!$preventRaceConditionSearchBar) {
-            goto(previousRoute);
+            await goto(previousRoute);
           }
           $preventRaceConditionSearchBar = false;
           return;
@@ -108,13 +109,13 @@
     return searchQuery ? JSON.parse(searchQuery) : {};
   })();
 
-  $: terms, onSearchQueryUpdate();
+  $: terms, handlePromiseError(onSearchQueryUpdate());
 
   async function onSearchQueryUpdate() {
     nextPage = 1;
     searchResultAssets = [];
     searchResultAlbums = [];
-    loadNextPage();
+    await loadNextPage();
   }
 
   export const loadNextPage = async () => {
diff --git a/web/src/routes/(user)/search/photos/[assetId]/+page.ts b/web/src/routes/(user)/search/photos/[assetId]/+page.ts
index 3c4bafa3ef..f1e5126931 100644
--- a/web/src/routes/(user)/search/photos/[assetId]/+page.ts
+++ b/web/src/routes/(user)/search/photos/[assetId]/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load = (async () => {
+export const load = (() => {
   redirect(302, AppRoute.SEARCH);
 }) satisfies PageLoad;
diff --git a/web/src/routes/(user)/sharing/+page.svelte b/web/src/routes/(user)/sharing/+page.svelte
index 602a3a2e01..b50cb5089b 100644
--- a/web/src/routes/(user)/sharing/+page.svelte
+++ b/web/src/routes/(user)/sharing/+page.svelte
@@ -19,7 +19,7 @@
   const createSharedAlbum = async () => {
     try {
       const newAlbum = await createAlbum({ createAlbumDto: { albumName: '' } });
-      goto(`${AppRoute.ALBUMS}/${newAlbum.id}`);
+      await goto(`${AppRoute.ALBUMS}/${newAlbum.id}`);
     } catch (error) {
       handleError(error, 'Unable to create album');
     }
diff --git a/web/src/routes/(user)/sharing/sharedlinks/+page.svelte b/web/src/routes/(user)/sharing/sharedlinks/+page.svelte
index 394e74faf8..3a6ff6ac0a 100644
--- a/web/src/routes/(user)/sharing/sharedlinks/+page.svelte
+++ b/web/src/routes/(user)/sharing/sharedlinks/+page.svelte
@@ -40,7 +40,7 @@
       deleteLinkId = null;
       await refresh();
     } catch (error) {
-      await handleError(error, 'Unable to delete shared link');
+      handleError(error, 'Unable to delete shared link');
     }
   };
 
diff --git a/web/src/routes/(user)/trash/+page.svelte b/web/src/routes/(user)/trash/+page.svelte
index 3dc126d7b6..c2398563ec 100644
--- a/web/src/routes/(user)/trash/+page.svelte
+++ b/web/src/routes/(user)/trash/+page.svelte
@@ -24,10 +24,11 @@
   import { emptyTrash, restoreTrash } from '@immich/sdk';
   import { mdiDeleteOutline, mdiHistory } from '@mdi/js';
   import type { PageData } from './$types';
+  import { handlePromiseError } from '$lib/utils';
 
   export let data: PageData;
 
-  $: $featureFlags.trash || goto(AppRoute.PHOTOS);
+  $featureFlags.trash || handlePromiseError(goto(AppRoute.PHOTOS));
 
   const assetStore = new AssetStore({ isTrashed: true });
   const assetInteractionStore = createAssetInteractionStore();
diff --git a/web/src/routes/(user)/trash/photos/[assetId]/+page.ts b/web/src/routes/(user)/trash/photos/[assetId]/+page.ts
index 0474207e32..eb3a453d24 100644
--- a/web/src/routes/(user)/trash/photos/[assetId]/+page.ts
+++ b/web/src/routes/(user)/trash/photos/[assetId]/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load = (async () => {
+export const load = (() => {
   redirect(302, AppRoute.TRASH);
 }) satisfies PageLoad;
diff --git a/web/src/routes/+layout.ts b/web/src/routes/+layout.ts
index ab0a19f1c2..ffb36cf352 100644
--- a/web/src/routes/+layout.ts
+++ b/web/src/routes/+layout.ts
@@ -3,7 +3,7 @@ import type { LayoutLoad } from './$types';
 export const ssr = false;
 export const csr = true;
 
-export const load = (async () => {
+export const load = (() => {
   return {
     meta: {
       title: 'Immich',
diff --git a/web/src/routes/admin/+page.ts b/web/src/routes/admin/+page.ts
index e4f090a069..0d53c4ef2b 100644
--- a/web/src/routes/admin/+page.ts
+++ b/web/src/routes/admin/+page.ts
@@ -2,6 +2,6 @@ import { AppRoute } from '$lib/constants';
 import { redirect } from '@sveltejs/kit';
 import type { PageLoad } from './$types';
 
-export const load = (async () => {
+export const load = (() => {
   redirect(302, AppRoute.ADMIN_USER_MANAGEMENT);
 }) satisfies PageLoad;
diff --git a/web/src/routes/admin/jobs-status/+page.svelte b/web/src/routes/admin/jobs-status/+page.svelte
index 38ecaa62f5..2f6c98af13 100644
--- a/web/src/routes/admin/jobs-status/+page.svelte
+++ b/web/src/routes/admin/jobs-status/+page.svelte
@@ -4,6 +4,7 @@
   import Icon from '$lib/components/elements/icon.svelte';
   import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
   import { AppRoute } from '$lib/constants';
+  import { asyncTimeout } from '$lib/utils';
   import { getAllJobsStatus, type AllJobStatusResponseDto } from '@immich/sdk';
   import { mdiCog } from '@mdi/js';
   import { onDestroy, onMount } from 'svelte';
@@ -11,21 +12,19 @@
 
   export let data: PageData;
 
-  let timer: ReturnType<typeof setInterval>;
-
   let jobs: AllJobStatusResponseDto;
 
-  const load = async () => {
-    jobs = await getAllJobsStatus();
-  };
+  let running = true;
 
   onMount(async () => {
-    await load();
-    timer = setInterval(load, 5000);
+    while (running) {
+      jobs = await getAllJobsStatus();
+      await asyncTimeout(5000);
+    }
   });
 
   onDestroy(() => {
-    clearInterval(timer);
+    running = false;
   });
 </script>
 
diff --git a/web/src/routes/admin/server-status/+page.svelte b/web/src/routes/admin/server-status/+page.svelte
index bf2830f15b..54f62b3adb 100644
--- a/web/src/routes/admin/server-status/+page.svelte
+++ b/web/src/routes/admin/server-status/+page.svelte
@@ -4,19 +4,21 @@
   import { getServerStatistics } from '@immich/sdk';
   import { onDestroy, onMount } from 'svelte';
   import type { PageData } from './$types';
+  import { asyncTimeout } from '$lib/utils';
 
   export let data: PageData;
 
-  let setIntervalHandler: ReturnType<typeof setInterval>;
+  let running = true;
 
   onMount(async () => {
-    setIntervalHandler = setInterval(async () => {
+    while (running) {
       data.stats = await getServerStatistics();
-    }, 5000);
+      await asyncTimeout(5000);
+    }
   });
 
   onDestroy(() => {
-    clearInterval(setIntervalHandler);
+    running = false;
   });
 </script>
 
diff --git a/web/src/routes/admin/user-management/+page.svelte b/web/src/routes/admin/user-management/+page.svelte
index 73237915b6..a3a93e0f35 100644
--- a/web/src/routes/admin/user-management/+page.svelte
+++ b/web/src/routes/admin/user-management/+page.svelte
@@ -51,7 +51,7 @@
     shouldShowCreateUserForm = false;
   };
 
-  const editUserHandler = async (user: UserResponseDto) => {
+  const editUserHandler = (user: UserResponseDto) => {
     selectedUser = user;
     shouldShowEditUserForm = true;
   };
@@ -67,7 +67,7 @@
     shouldShowInfoPanel = true;
   };
 
-  const deleteUserHandler = async (user: UserResponseDto) => {
+  const deleteUserHandler = (user: UserResponseDto) => {
     selectedUser = user;
     shouldShowDeleteConfirmDialog = true;
   };
@@ -82,7 +82,7 @@
     shouldShowDeleteConfirmDialog = false;
   };
 
-  const restoreUserHandler = async (user: UserResponseDto) => {
+  const restoreUserHandler = (user: UserResponseDto) => {
     selectedUser = user;
     shouldShowRestoreDialog = true;
   };
diff --git a/web/src/routes/auth/change-password/+page.svelte b/web/src/routes/auth/change-password/+page.svelte
index f56169ddb7..277b3c0040 100644
--- a/web/src/routes/auth/change-password/+page.svelte
+++ b/web/src/routes/auth/change-password/+page.svelte
@@ -3,10 +3,17 @@
   import ChangePasswordForm from '$lib/components/forms/change-password-form.svelte';
   import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
   import { AppRoute } from '$lib/constants';
-  import { user } from '$lib/stores/user.store';
+  import { resetSavedUser, user } from '$lib/stores/user.store';
+  import { logout } from '@immich/sdk';
   import type { PageData } from './$types';
 
   export let data: PageData;
+
+  const onSuccess = async () => {
+    await goto(AppRoute.AUTH_LOGIN);
+    resetSavedUser();
+    await logout();
+  };
 </script>
 
 <FullscreenContainer title={data.meta.title}>
@@ -18,5 +25,5 @@
     enter the new password below.
   </p>
 
-  <ChangePasswordForm user={$user} on:success={() => goto(AppRoute.AUTH_LOGIN)} />
+  <ChangePasswordForm user={$user} on:success={onSuccess} />
 </FullscreenContainer>
diff --git a/web/src/routes/auth/login/+page.svelte b/web/src/routes/auth/login/+page.svelte
index 199cd06e36..9c22439c56 100644
--- a/web/src/routes/auth/login/+page.svelte
+++ b/web/src/routes/auth/login/+page.svelte
@@ -1,21 +1,12 @@
 <script lang="ts">
-  import { afterNavigate, goto } from '$app/navigation';
+  import { goto } from '$app/navigation';
   import LoginForm from '$lib/components/forms/login-form.svelte';
   import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
   import { AppRoute } from '$lib/constants';
   import { featureFlags, serverConfig } from '$lib/stores/server-config.store';
-  import { resetSavedUser } from '$lib/stores/user.store';
-  import { logout } from '@immich/sdk';
   import type { PageData } from './$types';
 
   export let data: PageData;
-
-  afterNavigate(async ({ from }) => {
-    if (from?.url?.pathname === AppRoute.AUTH_CHANGE_PASSWORD) {
-      resetSavedUser();
-      await logout();
-    }
-  });
 </script>
 
 {#if $featureFlags.loaded}
diff --git a/web/src/routes/auth/onboarding/+page.svelte b/web/src/routes/auth/onboarding/+page.svelte
index 4cf42d4d3d..09139a7f7e 100644
--- a/web/src/routes/auth/onboarding/+page.svelte
+++ b/web/src/routes/auth/onboarding/+page.svelte
@@ -29,17 +29,17 @@
   const handleDoneClicked = async () => {
     if (index >= onboardingSteps.length - 1) {
       await setAdminOnboarding();
-      goto(AppRoute.PHOTOS);
+      await goto(AppRoute.PHOTOS);
     } else {
       index++;
-      goto(`${AppRoute.AUTH_ONBOARDING}?${QueryParameter.ONBOARDING_STEP}=${onboardingSteps[index].name}`);
+      await goto(`${AppRoute.AUTH_ONBOARDING}?${QueryParameter.ONBOARDING_STEP}=${onboardingSteps[index].name}`);
     }
   };
 
-  const handlePrevious = () => {
+  const handlePrevious = async () => {
     if (index >= 1) {
       index--;
-      goto(`${AppRoute.AUTH_ONBOARDING}?${QueryParameter.ONBOARDING_STEP}=${onboardingSteps[index].name}`);
+      await goto(`${AppRoute.AUTH_ONBOARDING}?${QueryParameter.ONBOARDING_STEP}=${onboardingSteps[index].name}`);
     }
   };
 </script>
diff --git a/web/svelte.config.js b/web/svelte.config.js
index 0081e8e76b..3cb982c6b8 100644
--- a/web/svelte.config.js
+++ b/web/svelte.config.js
@@ -4,12 +4,6 @@ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
 /** @type {import('@sveltejs/kit').Config} */
 const config = {
   preprocess: vitePreprocess(),
-  onwarn: (warning, handler) => {
-    if (warning.code.includes('a11y')) {
-      return;
-    }
-    handler(warning);
-  },
   kit: {
     adapter: adapter({
       // default options are shown. On some platforms