immich-app/immich · error · Exception
User must be logged in to access locked folder
Error message
User must be logged in to access locked folder
What it means
This exception is thrown by the locked folder page's timelineServiceProvider override when currentUserProvider is null. The locked-folder timeline is user-scoped (timelineFactoryProvider.lockedFolder(user.id)), so it cannot be constructed without an authenticated user. The throw indicates the page was built outside of a valid login session.
Solutions
- Add an auth guard so the locked folder route is unreachable while currentUser is null.
- Re-check the account session before unlocking the folder (e.g. reauthentication should also refresh currentUserProvider).
- Verify auth bootstrap/restoration completes before restoring navigation state to this page.
- Log the user out fully and redirect to login when the session is detected as expired.
Example fix
// before
GoRouter(routes: [
GoRoute(path: '/locked-folder', builder: (_, __) => const LockedFolderPage()),
])
// after
GoRouter(routes: [
GoRoute(path: '/locked-folder', redirect: (context, state) {
final user = ref.read(currentUserProvider);
return user == null ? '/login' : null;
}, builder: (_, __) => const LockedFolderPage()),
]) Defensive patterns
Strategy: validation
Validate before calling
final user = ref.read(currentUserProvider);
if (user == null) {
context.go('/login');
return;
} Type guard
bool isSignedIn(User? user) => user != null && user.id.isNotEmpty;
Try / catch
try {
final service = ref.read(timelineServiceProvider);
} catch (e) {
if (e.toString().contains('User must be logged in')) {
context.go('/login');
} else {
rethrow;
}
} Prevention
- Require a valid account session before the folder unlock flow (unlock should reconfirm, not replace, sign-in).
- Guard the locked-folder route with a redirect to login when currentUser is null.
- Restore navigation state only after auth bootstrap completes.
- Handle session expiry events by navigating to login immediately.
- Add a signed-out deep-link test for this page.
When it happens
Trigger: Building the locked folder page while currentUserProvider returns null — auth not yet initialized, session expired, or the route was pushed without a login gate.
Common situations: Opening the locked folder via deep link before sign-in completes; biometric/PIN unlock flow that re-authenticates the folder but the underlying account session already expired; auth stream error leaving currentUser permanently null.
Related errors
- User must be logged in to access archive
- User must be logged in to access favorite
- User must be logged in to access recently taken
- User must be logged in to view person timeline
- User must be logged in to access archive
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/107ccac22d64a0e0.
Report an issue: GitHub.
Appendix: source
Thrown at mobile/lib/presentation/pages/locked_folder.page.dart:60
}
if (state == AppLifecycleState.paused) {
unawaited(ref.read(authProvider.notifier).lockPinCode());
unawaited(context.navigateTo(const TabShellRoute()));
return;
}
setState(() {
_showOverlay = state != AppLifecycleState.resumed;
});
}
@override
Widget build(BuildContext context) {
return ProviderScope(
overrides: [
timelineServiceProvider.overrideWith((ref) {
final user = ref.watch(currentUserProvider);
if (user == null) {
throw Exception('User must be logged in to access locked folder');
}
final timelineService = ref.watch(timelineFactoryProvider).lockedFolder(user.id);
ref.onDispose(timelineService.dispose);
return timelineService;
}),
],
child: _showOverlay
? const SizedBox()
: PopScope(
onPopInvokedWithResult: (didPop, _) => didPop ? ref.read(authProvider.notifier).lockPinCode() : null,
child: Timeline(
appBar: MesmerizingSliverAppBar(title: context.t.locked_folder),
bottomSheet: const LockedFolderBottomSheet(),
),
),
);
}View on GitHub (pinned to e55ac299a4)