immich-app/immich · error · Exception
User must be logged in to access archive
Error message
User must be logged in to access archive
What it means
This exception is thrown by the archive page's timelineServiceProvider override in Riverpod when the currentUserProvider yields null. The app guards every timeline-backed page with an explicit login check: building the archive timeline service requires a non-null User because the archive timeline is keyed by user.id. If the auth state has not resolved to a logged-in user when the provider is first read, the widget tree build aborts with this error.
Solutions
- Ensure the app shows a login screen until currentUserProvider emits a non-null user (gate navigation on auth state).
- Wrap the archive route in a redirect/guard that redirects unauthenticated users to the login page instead of building the page.
- Verify currentUserProvider is wired to the real auth state source (e.g. auth state changes stream) and not left as a stub returning null.
- Restore/persist the session on app start so a previously logged-in user is available immediately at first build.
Example fix
// before
Navigator.push(context, ArchivePage.route());
// after
final user = ref.read(currentUserProvider);
if (user == null) {
Navigator.push(context, LoginPage.route(returnTo: ArchivePage.route()));
} else {
Navigator.push(context, ArchivePage.route());
} 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
- Route all timeline pages behind an auth-aware router redirect.
- Await session restoration before rendering the home shell.
- Watch currentUserProvider in a top-level builder that swaps to a login screen when null.
- Test deep links with a signed-out session to catch ungated routes.
- Write integration tests asserting no timeline provider is read while logged out.
When it happens
Trigger: Navigating to the archive page while currentUserProvider returns null — e.g. the auth stream has not yet emitted, the session was logged out, or the page is pushed before the app's login gate completes.
Common situations: Cold-start deep links straight to /archive before authentication initializes; token expiry causing the auth listener to clear the user while the page remains on the navigation stack; a misconfigured auth bootstrap (e.g. restore-session call skipped) so currentUser is always null.
Related errors
- User must be logged in to access favorite
- User must be logged in to access locked folder
- 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/a8e3db847d82d84f.
Report an issue: GitHub.
Appendix: source
Thrown at mobile/lib/presentation/pages/archive.page.dart:22
import 'package:immich_mobile/generated/translations.g.dart';
import 'package:immich_mobile/presentation/widgets/bottom_sheet/archive_bottom_sheet.widget.dart';
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
class ArchivePage extends StatelessWidget {
const ArchivePage({super.key});
@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 archive');
}
final timelineService = ref.watch(timelineFactoryProvider).archive(user.id);
ref.onDispose(timelineService.dispose);
return timelineService;
}),
],
child: Timeline(
appBar: MesmerizingSliverAppBar(title: context.t.archive, icon: Icons.archive_outlined),
bottomSheet: const ArchiveBottomSheet(),
),
);
}
}
View on GitHub (pinned to e55ac299a4)