diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt
index 73f32474b..9b7dfea73 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt
@@ -1,6 +1,7 @@
 package eu.kanade.presentation.browse
 
 import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.HelpOutline
@@ -11,6 +12,7 @@ import androidx.compose.material3.SnackbarHostState
 import androidx.compose.material3.SnackbarResult
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.paging.LoadState
 import androidx.paging.compose.LazyPagingItems
@@ -19,12 +21,15 @@ import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid
 import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
 import eu.kanade.presentation.browse.components.BrowseSourceEHentaiList
 import eu.kanade.presentation.browse.components.BrowseSourceList
+import eu.kanade.presentation.components.AppBar
 import eu.kanade.presentation.components.EmptyScreen
 import eu.kanade.presentation.components.EmptyScreenAction
 import eu.kanade.presentation.components.LoadingScreen
+import eu.kanade.presentation.components.Scaffold
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.source.CatalogueSource
 import eu.kanade.tachiyomi.source.LocalSource
+import eu.kanade.tachiyomi.source.Source
+import eu.kanade.tachiyomi.source.SourceManager
 import exh.metadata.metadata.base.RaisedSearchMetadata
 import exh.source.isEhBasedSource
 import kotlinx.coroutines.flow.StateFlow
@@ -33,7 +38,7 @@ import tachiyomi.domain.manga.model.Manga
 
 @Composable
 fun BrowseSourceContent(
-    source: CatalogueSource?,
+    source: Source?,
     mangaList: LazyPagingItems<StateFlow</* SY --> */Pair<Manga, RaisedSearchMetadata?>/* SY <-- */>>,
     columns: GridCells,
     // SY -->
@@ -169,3 +174,24 @@ fun BrowseSourceContent(
         }
     }
 }
+
+@Composable
+fun MissingSourceScreen(
+    source: SourceManager.StubSource,
+    navigateUp: () -> Unit,
+) {
+    Scaffold(
+        topBar = { scrollBehavior ->
+            AppBar(
+                title = source.name,
+                navigateUp = navigateUp,
+                scrollBehavior = scrollBehavior,
+            )
+        },
+    ) { paddingValues ->
+        EmptyScreen(
+            message = source.getSourceNotInstalledException().message!!,
+            modifier = Modifier.padding(paddingValues),
+        )
+    }
+}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt
index 4e74e4f92..7ca9cfe30 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt
@@ -20,9 +20,9 @@ import eu.kanade.presentation.components.DropdownMenu
 import eu.kanade.presentation.components.RadioMenuItem
 import eu.kanade.presentation.components.SearchToolbar
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.source.CatalogueSource
 import eu.kanade.tachiyomi.source.ConfigurableSource
 import eu.kanade.tachiyomi.source.LocalSource
+import eu.kanade.tachiyomi.source.Source
 import exh.source.anyIs
 import tachiyomi.domain.library.model.LibraryDisplayMode
 
@@ -30,7 +30,7 @@ import tachiyomi.domain.library.model.LibraryDisplayMode
 fun BrowseSourceToolbar(
     searchQuery: String?,
     onSearchQueryChange: (String?) -> Unit,
-    source: CatalogueSource?,
+    source: Source?,
     displayMode: LibraryDisplayMode?,
     onDisplayModeChange: (LibraryDisplayMode) -> Unit,
     navigateUp: () -> Unit,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt
index 69aa88d09..2de99a437 100755
--- a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt
@@ -248,7 +248,7 @@ class SourceManager(
         }
     }
 
-    inner class SourceNotInstalledException(val sourceString: String) :
+    inner class SourceNotInstalledException(sourceString: String) :
         Exception(context.getString(R.string.source_not_installed, sourceString))
 
     // SY -->
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt
index 644ec761b..fcc148dbb 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt
@@ -39,6 +39,7 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey
 import cafe.adriel.voyager.navigator.LocalNavigator
 import cafe.adriel.voyager.navigator.currentOrThrow
 import eu.kanade.presentation.browse.BrowseSourceContent
+import eu.kanade.presentation.browse.MissingSourceScreen
 import eu.kanade.presentation.browse.components.BrowseSourceToolbar
 import eu.kanade.presentation.browse.components.FailedToLoadSavedSearchDialog
 import eu.kanade.presentation.browse.components.RemoveMangaDialog
@@ -51,7 +52,9 @@ import eu.kanade.presentation.components.Scaffold
 import eu.kanade.presentation.util.AssistContentScreen
 import eu.kanade.presentation.util.padding
 import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.source.CatalogueSource
 import eu.kanade.tachiyomi.source.LocalSource
+import eu.kanade.tachiyomi.source.SourceManager
 import eu.kanade.tachiyomi.source.online.HttpSource
 import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
 import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
@@ -83,12 +86,6 @@ data class BrowseSourceScreen(
 
     @Composable
     override fun Content() {
-        val navigator = LocalNavigator.currentOrThrow
-        val scope = rememberCoroutineScope()
-        val context = LocalContext.current
-        val haptic = LocalHapticFeedback.current
-        val uriHandler = LocalUriHandler.current
-
         val screenModel = rememberScreenModel {
             BrowseSourceScreenModel(
                 sourceId = sourceId,
@@ -101,8 +98,7 @@ data class BrowseSourceScreen(
         }
         val state by screenModel.state.collectAsState()
 
-        val snackbarHostState = remember { SnackbarHostState() }
-
+        val navigator = LocalNavigator.currentOrThrow
         val navigateUp: () -> Unit = {
             when {
                 !state.isUserQuery && state.toolbarQuery != null -> screenModel.setToolbarQuery(null)
@@ -110,8 +106,21 @@ data class BrowseSourceScreen(
             }
         }
 
-        val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) }
+        if (screenModel.source is SourceManager.StubSource) {
+            MissingSourceScreen(
+                source = screenModel.source,
+                navigateUp = navigateUp,
+            )
+            return
+        }
 
+        val scope = rememberCoroutineScope()
+        val context = LocalContext.current
+        val haptic = LocalHapticFeedback.current
+        val uriHandler = LocalUriHandler.current
+        val snackbarHostState = remember { SnackbarHostState() }
+
+        val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) }
         val onWebViewClick = f@{
             val source = screenModel.source as? HttpSource ?: return@f
             navigator.push(
@@ -169,7 +178,7 @@ data class BrowseSourceScreen(
                                 Text(text = stringResource(R.string.popular))
                             },
                         )
-                        if (screenModel.source.supportsLatest) {
+                        if ((screenModel.source as CatalogueSource).supportsLatest) {
                             FilterChip(
                                 selected = state.listing == Listing.Latest,
                                 onClick = {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
index c848d11c6..f69d445c9 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
@@ -143,7 +143,7 @@ open class BrowseSourceScreenModel(
 
     var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
 
-    val source = sourceManager.get(sourceId) as CatalogueSource
+    val source = sourceManager.getOrStub(sourceId)
 
     // SY -->
     val ehentaiBrowseDisplayMode by unsortedPreferences.enhancedEHentaiView().asState(coroutineScope)
@@ -152,20 +152,22 @@ open class BrowseSourceScreenModel(
     // SY <--
 
     init {
-        mutableState.update {
-            var query: String? = null
-            var listing = it.listing
+        if (source is CatalogueSource) {
+            mutableState.update {
+                var query: String? = null
+                var listing = it.listing
 
-            if (listing is Listing.Search) {
-                query = listing.query
-                listing = Listing.Search(query, source.getFilterList())
+                if (listing is Listing.Search) {
+                    query = listing.query
+                    listing = Listing.Search(query, source.getFilterList())
+                }
+
+                it.copy(
+                    listing = listing,
+                    filters = source.getFilterList(),
+                    toolbarQuery = query,
+                )
             }
-
-            it.copy(
-                listing = listing,
-                filters = source.getFilterList(),
-                toolbarQuery = query,
-            )
         }
 
         // SY -->
@@ -185,14 +187,16 @@ open class BrowseSourceScreenModel(
             }
         }
 
-        getExhSavedSearch.subscribe(source.id, source::getFilterList)
-            .onEach { savedSearches ->
-                mutableState.update { it.copy(savedSearches = savedSearches) }
-                withUIContext {
-                    filterSheet?.setSavedSearches(savedSearches)
+        if (source is CatalogueSource) {
+            getExhSavedSearch.subscribe(source.id, source::getFilterList)
+                .onEach { savedSearches ->
+                    mutableState.update { it.copy(savedSearches = savedSearches) }
+                    withUIContext {
+                        filterSheet?.setSavedSearches(savedSearches)
+                    }
                 }
-            }
-            .launchIn(coroutineScope)
+                .launchIn(coroutineScope)
+        }
         // SY <--
     }
 
@@ -252,6 +256,8 @@ open class BrowseSourceScreenModel(
     // SY <--
 
     fun resetFilters() {
+        if (source !is CatalogueSource) return
+
         mutableState.update { it.copy(filters = source.getFilterList()) }
     }
 
@@ -260,6 +266,7 @@ open class BrowseSourceScreenModel(
     }
 
     fun search(query: String? = null, filters: FilterList? = null) {
+        if (source !is CatalogueSource) return
         // SY -->
         if (filters != null && filters !== state.value.filters) {
             mutableState.update { state -> state.copy(filters = filters) }
@@ -280,6 +287,8 @@ open class BrowseSourceScreenModel(
     }
 
     fun searchGenre(genreName: String) {
+        if (source !is CatalogueSource) return
+
         val defaultFilters = source.getFilterList()
         var genreExists = false
 
@@ -467,6 +476,7 @@ open class BrowseSourceScreenModel(
     }
 
     open fun initFilterSheet(context: Context, navigator: Navigator) {
+        source as? CatalogueSource ?: return
         val state = state.value
         /*if (state.filters.isEmpty()) {
             return
@@ -575,6 +585,7 @@ open class BrowseSourceScreenModel(
     fun saveSearch(
         name: String,
     ) {
+        if (source !is CatalogueSource) return
         coroutineScope.launchNonCancellable {
             val query = state.value.listing.query
             val filterList = state.value.listing.filters.ifEmpty { source.getFilterList() }
@@ -596,11 +607,15 @@ open class BrowseSourceScreenModel(
         }
     }
 
-    suspend fun loadSearch(searchId: Long) =
-        getExhSavedSearch.awaitOne(searchId, source::getFilterList)
+    suspend fun loadSearch(searchId: Long): EXHSavedSearch? {
+        if (source !is CatalogueSource) return null
+        return getExhSavedSearch.awaitOne(searchId, source::getFilterList)
+    }
 
-    suspend fun loadSearches() =
-        getExhSavedSearch.await(source.id, source::getFilterList)
+    suspend fun loadSearches(): List<EXHSavedSearch> {
+        if (source !is CatalogueSource) return emptyList()
+        return getExhSavedSearch.await(source.id, source::getFilterList)
+    }
     // EXH <--
 }
 
diff --git a/app/src/main/java/exh/recs/RecommendsScreenModel.kt b/app/src/main/java/exh/recs/RecommendsScreenModel.kt
index 88743d7cc..5f539c6cd 100644
--- a/app/src/main/java/exh/recs/RecommendsScreenModel.kt
+++ b/app/src/main/java/exh/recs/RecommendsScreenModel.kt
@@ -2,6 +2,7 @@ package exh.recs
 
 import eu.kanade.domain.manga.interactor.GetManga
 import eu.kanade.domain.source.model.SourcePagingSourceType
+import eu.kanade.tachiyomi.source.CatalogueSource
 import eu.kanade.tachiyomi.source.model.FilterList
 import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel
 import kotlinx.coroutines.runBlocking
@@ -17,6 +18,6 @@ class RecommendsScreenModel(
     val manga = runBlocking { getManga.await(mangaId) }!!
 
     override fun createSourcePagingSource(query: String, filters: FilterList): SourcePagingSourceType {
-        return RecommendsPagingSource(source, manga)
+        return RecommendsPagingSource(source as CatalogueSource, manga)
     }
 }