{"record":{"id":"29916fc0e1526b2f","repo":"immich-app/immich","slug":"user-must-be-logged-in-to-access-archive-map-bottom-sheet","errorCode":null,"errorMessage":"User must be logged in to access archive","messagePattern":"User must be logged in to access archive","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart","lineNumber":49,"sourceCode":"      backgroundColor: context.themeData.colorScheme.surface,\n      slivers: const [\n        SliverFillRemaining(hasScrollBody: false, child: SizedBox(height: 0, child: _ScopedMapTimeline())),\n      ],\n    );\n  }\n}\n\nclass _ScopedMapTimeline extends StatelessWidget {\n  const _ScopedMapTimeline();\n\n  @override\n  Widget build(BuildContext context) {\n    return ProviderScope(\n      overrides: [\n        timelineServiceProvider.overrideWith((ref) {\n          final user = ref.watch(currentUserProvider);\n          if (user == null) {\n            throw Exception('User must be logged in to access archive');\n          }\n\n          final withPartners = ref.watch(mapStateProvider.select((s) => s.withPartners));\n          final users = withPartners ? ref.watch(timelineUsersProvider).valueOrNull ?? [user.id] : [user.id];\n\n          final optionsController = StreamController<TimelineMapOptions>.broadcast();\n          ref.onDispose(optionsController.close);\n\n          var currentOptions = ref.read(mapStateProvider).toOptions();\n\n          ref.listen(mapStateProvider.select((state) => state.toOptions()), (_, newOptions) {\n            currentOptions = newOptions;\n            optionsController.add(newOptions);\n          });\n\n          final timelineService = ref\n              .watch(timelineFactoryProvider)\n              .geographicMap(users, () => currentOptions, optionsController.stream);","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/mobile/lib/presentation/widgets/bottom_sheet/map_bottom_sheet.widget.dart#L31-L67","documentation":"Thrown by the timelineServiceProvider override in the map bottom sheet when currentUserProvider is null. Building the map timeline (users list, TimelineMapOptions stream) requires a user id, so an unauthenticated session makes the provider throw during build. It indicates the map sheet was opened without a logged-in user.","triggerScenarios":"Opening the map bottom sheet while currentUserProvider is null — before login finishes, after logout/token expiry, or when auth state restoration hasn't completed when the sheet builds.","commonSituations":"Session expired in background then user taps the map button; deep link opening the map pre-login; stale credentials after a server-side logout.","solutions":["Only open the map sheet when a user is authenticated (check currentUserProvider first).","Await the auth AsyncValue before building the sheet's provider overrides.","Re-authenticate on this error and reopen the sheet.","Render a login prompt / empty state instead of throwing when user is null."],"exampleFix":"// before\nfinal user = ref.watch(currentUserProvider);\nif (user == null) {\n  throw Exception('User must be logged in to access archive');\n}\n// after\nfinal user = ref.watch(currentUserProvider);\nif (user == null) {\n  return const SizedBox.shrink(); // or redirect to login\n}","handlingStrategy":"try-catch","validationCode":"final user = ref.watch(currentUserProvider);\nif (user == null) {\n  context.go('/login');\n  return;\n}","typeGuard":"bool isAuthenticated(User? u) => u != null && u.id.isNotEmpty;","tryCatchPattern":"try {\n  final timeline = ref.watch(timelineServiceProvider);\n  // open map sheet\n} catch (e) {\n  if (e.toString().contains('User must be logged in')) {\n    context.go('/login');\n  } else {\n    rethrow;\n  }\n}","preventionTips":["Check currentUserProvider before opening the map bottom sheet","Await auth restoration before user-scoped provider overrides","Silently refresh expired sessions","Render login prompt instead of throwing in build overrides"],"tags":["authentication","flutter","riverpod","map"],"backgroundTag":"authentication-required","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}