{"record":{"id":"9139097fba4754e2","repo":"immich-app/immich","slug":"user-must-be-logged-in-to-video","errorCode":null,"errorMessage":"User must be logged in to video","messagePattern":"User must be logged in to video","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"mobile/lib/presentation/pages/video.page.dart","lineNumber":21,"sourceCode":"import 'package:hooks_riverpod/hooks_riverpod.dart';\nimport 'package:immich_mobile/generated/translations.g.dart';\nimport 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';\nimport 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';\nimport 'package:immich_mobile/providers/user.provider.dart';\nimport 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';\n\n@RoutePage()\nclass VideoPage extends StatelessWidget {\n  const VideoPage({super.key});\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 video');\n          }\n\n          final timelineService = ref.watch(timelineFactoryProvider).video(user.id);\n          ref.onDispose(timelineService.dispose);\n          return timelineService;\n        }),\n      ],\n      child: Timeline(appBar: MesmerizingSliverAppBar(title: context.t.videos)),\n    );\n  }\n}\n","sourceCodeStart":3,"sourceCodeEnd":33,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/mobile/lib/presentation/pages/video.page.dart#L3-L33","documentation":"Thrown by the timelineServiceProvider override in the Video page when currentUserProvider is null. The video timeline needs a user id (timelineFactoryProvider.video(user.id)), so an unauthenticated session makes the provider throw during initialization. It means the Video page was built without a logged-in user.","triggerScenarios":"Opening the video page before login completes, after logout/token expiry, or when auth state restoration races with page navigation so currentUserProvider reads null.","commonSituations":"Expired session token; deep link to a video opened pre-login; app resumed from background after the session was invalidated.","solutions":["Ensure authentication before navigating to the video page.","Make the page await the resolved auth AsyncValue instead of reading a possibly-null user.","Re-authenticate (token refresh or login) on this error and retry navigation.","Return a login prompt widget from the override when the user is null."],"exampleFix":"// before\nfinal user = ref.watch(currentUserProvider);\nif (user == null) {\n  throw Exception('User must be logged in to video');\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  // play video timeline\n} catch (e) {\n  if (e.toString().contains('User must be logged in')) {\n    context.go('/login');\n  } else {\n    rethrow;\n  }\n}","preventionTips":["Route to video pages only through authenticated guards","Resolve auth state before user-scoped provider overrides run","Proactively refresh sessions on app resume","Fallback to a login screen when user is null"],"tags":["authentication","flutter","riverpod","state"],"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"}