diff --git a/lib/app.dart b/lib/app.dart
index ccab187add..8ec6f08876 100644
--- a/lib/app.dart
+++ b/lib/app.dart
@@ -8,10 +8,12 @@ import 'package:flutter_hooks/flutter_hooks.dart';
 import 'package:flutter_localizations/flutter_localizations.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart'
     hide Consumer, FutureProvider, Provider;
+import 'package:path/path.dart' as p;
 import 'package:provider/provider.dart';
 
 import 'account/notification_service.dart';
 import 'constants/brightness_theme_data.dart';
+import 'constants/constants.dart';
 import 'constants/resources.dart';
 import 'generated/l10n.dart';
 import 'ui/home/bloc/conversation_list_bloc.dart';
@@ -28,6 +30,7 @@ import 'ui/provider/mention_cache_provider.dart';
 import 'ui/provider/setting_provider.dart';
 import 'ui/provider/slide_category_provider.dart';
 import 'utils/extension/extension.dart';
+import 'utils/file.dart';
 import 'utils/hook.dart';
 import 'utils/logger.dart';
 import 'utils/platform.dart';
@@ -53,6 +56,38 @@ class App extends HookConsumerWidget {
     precacheImage(
         const AssetImage(Resources.assetsImagesChatBackgroundPng), context);
 
+    final appDatabaseInitError = ref.watch(appDatabaseInitErrorProvider);
+    if (appDatabaseInitError != null) {
+      var error = appDatabaseInitError;
+      if (error is DriftRemoteException) {
+        error = error.remoteCause;
+      }
+      if (error is SqliteException) {
+        return _App(
+          home: DatabaseOpenFailedPage(
+            error: error,
+            closeDatabaseCallback: () => ref.read(appDatabaseProvider).close(),
+            deleteDatabaseCallback: () => dropDatabaseFile(
+              mixinDocumentsDirectory.path,
+              kDbFileName,
+            ),
+            openDatabaseCallback: () async {
+              final db = ref.refresh(appDatabaseProvider);
+              try {
+                await db.settingKeyValue.initialize;
+                ref.read(appDatabaseInitErrorProvider.notifier).state = null;
+              } catch (e) {
+                w('reOpenDatabaseCallback error: $e');
+                ref.read(appDatabaseInitErrorProvider.notifier).state = e;
+              }
+            },
+          ),
+        );
+      } else {
+        return _App(home: OpenAppFailedPage(error: error));
+      }
+    }
+
     final initialized = useMemoizedFuture(
       () => ref.read(multiAuthStateNotifierProvider.notifier).initialized,
       null,
@@ -93,20 +128,24 @@ class _LoginApp extends HookConsumerWidget {
       }
       if (error is SqliteException) {
         return _App(
-          home: DatabaseOpenFailedPage(error: error),
+          home: DatabaseOpenFailedPage(
+            error: error,
+            openDatabaseCallback: () =>
+                ref.read(databaseProvider.notifier).open(),
+            closeDatabaseCallback: () =>
+                ref.read(databaseProvider.notifier).close(),
+            deleteDatabaseCallback: () async {
+              final identityNumber = context.account?.identityNumber;
+              if (identityNumber == null) return;
+              await dropDatabaseFile(
+                p.join(mixinDocumentsDirectory.path, identityNumber),
+                kDbFileName,
+              );
+            },
+          ),
         );
       } else {
-        return _App(
-          home: LandingFailedPage(
-              title: context.l10n.unknowError,
-              message: error.toString(),
-              actions: [
-                ElevatedButton(
-                  onPressed: () {},
-                  child: Text(context.l10n.exit),
-                )
-              ]),
-        );
+        return _App(home: OpenAppFailedPage(error: error));
       }
     }
 
diff --git a/lib/main.dart b/lib/main.dart
index 3c5aec4048..afc7113148 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -21,7 +21,6 @@ import 'package:window_size/window_size.dart';
 
 import 'app.dart';
 import 'bloc/custom_bloc_observer.dart';
-import 'db/app/app_database.dart';
 import 'ui/home/home.dart';
 import 'ui/provider/database_provider.dart';
 import 'utils/app_lifecycle.dart';
@@ -104,12 +103,15 @@ Future<void> main(List<String> args) async {
     Bloc.observer = CustomBlocObserver();
   }
 
-  final appDatabase = AppDatabase.connect(fromMainIsolate: true);
-  await appDatabase.settingKeyValue.initialize;
+  final container = ProviderContainer();
+  try {
+    await container.read(appDatabaseProvider).settingKeyValue.initialize;
+  } catch (error, stacktrace) {
+    e('failed to initialize setting key value: $error\n$stacktrace');
+    container.read(appDatabaseInitErrorProvider.notifier).state = error;
+  }
   runApp(ProviderScope(
-    overrides: [
-      appDatabaseProvider.overrideWithValue(appDatabase),
-    ],
+    parent: container,
     child: const OverlaySupport.global(child: App()),
   ));
 
diff --git a/lib/ui/landing/landing_failed.dart b/lib/ui/landing/landing_failed.dart
index 035e6870e0..ccf60859c8 100644
--- a/lib/ui/landing/landing_failed.dart
+++ b/lib/ui/landing/landing_failed.dart
@@ -5,25 +5,34 @@ import 'package:flutter/material.dart';
 import 'package:hooks_riverpod/hooks_riverpod.dart';
 import 'package:path/path.dart' as p;
 
-import '../../constants/constants.dart';
 import '../../utils/extension/extension.dart';
 import '../../utils/file.dart';
 import '../../widgets/dialog.dart';
-import '../provider/database_provider.dart';
 import 'landing.dart';
 
 // https://sqlite.org/rescode.html
 const _kSqliteCorrupt = 11;
 const _kSqliteLocked = 6;
 const _kSqliteNotADb = 26;
+const _kSqliteBusy = 5;
+
+typedef DeleteDatabaseCallback = Future<void> Function();
+typedef OpenDatabaseCallback = Future<void> Function();
+typedef CloseDatabaseCallback = Future<void> Function();
 
 class DatabaseOpenFailedPage extends StatelessWidget {
   const DatabaseOpenFailedPage({
     required this.error,
+    required this.openDatabaseCallback,
+    required this.deleteDatabaseCallback,
+    required this.closeDatabaseCallback,
     super.key,
   });
 
   final SqliteException error;
+  final OpenDatabaseCallback openDatabaseCallback;
+  final DeleteDatabaseCallback deleteDatabaseCallback;
+  final CloseDatabaseCallback closeDatabaseCallback;
 
   @override
   Widget build(BuildContext context) {
@@ -41,14 +50,31 @@ class DatabaseOpenFailedPage extends StatelessWidget {
     final canDeleteDatabase =
         const {_kSqliteCorrupt, _kSqliteNotADb}.contains(error.resultCode);
 
+    final canRetry = error.resultCode == _kSqliteBusy;
+
     return LandingFailedPage(
       title: context.l10n.failedToOpenDatabase,
       message: message,
       actions: [
         if (canDeleteDatabase)
-          const Padding(
-            padding: EdgeInsets.only(bottom: 16),
-            child: _RecreateDatabaseButton(),
+          Padding(
+            padding: const EdgeInsets.only(bottom: 16),
+            child: _RecreateDatabaseButton(
+              openDatabaseCallback: openDatabaseCallback,
+              deleteDatabaseCallback: deleteDatabaseCallback,
+              closeDatabaseCallback: closeDatabaseCallback,
+            ),
+          ),
+        if (canRetry)
+          Padding(
+            padding: const EdgeInsets.only(bottom: 16),
+            child: _Button(
+              onTap: () async {
+                await closeDatabaseCallback();
+                await openDatabaseCallback();
+              },
+              text: context.l10n.retry,
+            ),
           ),
         _Button(
           onTap: () {
@@ -62,14 +88,19 @@ class DatabaseOpenFailedPage extends StatelessWidget {
 }
 
 class _RecreateDatabaseButton extends HookConsumerWidget {
-  const _RecreateDatabaseButton();
+  const _RecreateDatabaseButton({
+    required this.openDatabaseCallback,
+    required this.deleteDatabaseCallback,
+    required this.closeDatabaseCallback,
+  });
+
+  final OpenDatabaseCallback openDatabaseCallback;
+  final DeleteDatabaseCallback deleteDatabaseCallback;
+  final CloseDatabaseCallback closeDatabaseCallback;
 
   @override
   Widget build(BuildContext context, WidgetRef ref) => TextButton(
         onPressed: () async {
-          final identityNumber = context.account?.identityNumber;
-          if (identityNumber == null) return;
-
           final result = await showConfirmMixinDialog(
             context,
             context.l10n.databaseRecreateTips,
@@ -78,23 +109,9 @@ class _RecreateDatabaseButton extends HookConsumerWidget {
           if (result != DialogEvent.positive) {
             return;
           }
-          await ref.read(databaseProvider.notifier).close();
-          // Rename the old database file to a new name with timestamp.
-          final now = DateTime.now();
-          renameFileWithTime(
-              p.join(mixinDocumentsDirectory.path, identityNumber,
-                  '$kDbFileName.db'),
-              now);
-          await Future.forEach(
-            [
-              File(p.join(mixinDocumentsDirectory.path, identityNumber,
-                  '$kDbFileName.db-shm')),
-              File(p.join(mixinDocumentsDirectory.path, identityNumber,
-                  '$kDbFileName.db-wal'))
-            ].where((e) => e.existsSync()),
-            (element) => element.delete(),
-          );
-          await ref.read(databaseProvider.notifier).open();
+          await closeDatabaseCallback();
+          await deleteDatabaseCallback();
+          await openDatabaseCallback();
         },
         child: Text(
           context.l10n.continueText,
@@ -105,6 +122,19 @@ class _RecreateDatabaseButton extends HookConsumerWidget {
       );
 }
 
+Future<void> dropDatabaseFile(String dbDir, String dbName) async {
+  // Rename the old database file to a new name with timestamp.
+  final now = DateTime.now();
+  renameFileWithTime(p.join(dbDir, '$dbName.db'), now);
+  await Future.forEach(
+    [
+      File(p.join(dbDir, '$dbName.db-shm')),
+      File(p.join(dbDir, '$dbName.db-wal'))
+    ].where((e) => e.existsSync()),
+    (element) => renameFileWithTime(element.path, now),
+  );
+}
+
 class _Button extends StatelessWidget {
   const _Button({required this.text, required this.onTap});
 
@@ -169,3 +199,26 @@ class LandingFailedPage extends StatelessWidget {
         ),
       );
 }
+
+/// Failed to open app with an unknown error.
+class OpenAppFailedPage extends StatelessWidget {
+  const OpenAppFailedPage({
+    required this.error,
+    super.key,
+  });
+
+  final dynamic error;
+
+  @override
+  Widget build(BuildContext context) => LandingFailedPage(
+          title: context.l10n.unknowError,
+          message: error.toString(),
+          actions: [
+            ElevatedButton(
+              onPressed: () {
+                exit(1);
+              },
+              child: Text(context.l10n.exit),
+            )
+          ]);
+}
diff --git a/lib/ui/provider/database_provider.dart b/lib/ui/provider/database_provider.dart
index 6431c08467..753f0a6688 100644
--- a/lib/ui/provider/database_provider.dart
+++ b/lib/ui/provider/database_provider.dart
@@ -12,8 +12,15 @@ import 'account/multi_auth_provider.dart';
 import 'hive_key_value_provider.dart';
 import 'slide_category_provider.dart';
 
-final appDatabaseProvider =
-    Provider<AppDatabase>((ref) => throw UnimplementedError());
+final appDatabaseProvider = StateProvider<AppDatabase>(
+  (ref) {
+    final db = AppDatabase.connect(fromMainIsolate: true);
+    ref.onDispose(db.close);
+    return db;
+  },
+);
+
+final appDatabaseInitErrorProvider = StateProvider<dynamic>((ref) => null);
 
 final databaseProvider =
     StateNotifierProvider.autoDispose<DatabaseOpener, AsyncValue<Database>>(
diff --git a/lib/utils/db/db_key_value.dart b/lib/utils/db/db_key_value.dart
index 098f7fe350..a5c581007a 100644
--- a/lib/utils/db/db_key_value.dart
+++ b/lib/utils/db/db_key_value.dart
@@ -56,7 +56,7 @@ typedef AppKeyValue = _BaseDbKeyValue<AppPropertyGroup>;
 class _BaseDbKeyValue<G> extends ChangeNotifier {
   _BaseDbKeyValue({required this.group, required KeyValueDao<G> dao})
       : _dao = dao {
-    _loadProperties().whenComplete(_initCompleter.complete);
+    _initCompleter.complete(_loadProperties());
     _subscription = dao.watchTableHasChanged(group).listen((event) {
       _loadProperties();
     });