{"record":{"id":"38ee698fa0033def","repo":"immich-app/immich","slug":"user-must-be-logged-in-to-view-person-timeline","errorCode":null,"errorMessage":"User must be logged in to view person timeline","messagePattern":"User must be logged in to view person timeline","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"mobile/lib/presentation/pages/person.page.dart","lineNumber":82,"sourceCode":"          },\n          onEditBirthday: () async {\n            await handleEditBirthday(context);\n            ContextHelper(context).pop();\n          },\n          birthdayExists: _person.birthDate != null,\n        );\n      },\n    );\n  }\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 view person timeline');\n          }\n\n          final timelineService = ref.watch(timelineFactoryProvider).person(user.id, _person.id);\n          ref.onDispose(timelineService.dispose);\n          return timelineService;\n        }),\n      ],\n      child: Timeline(\n        appBar: PersonSliverAppBar(\n          person: _person,\n          onNameTap: () => handleEditName(context),\n          onBirthdayTap: () => handleEditBirthday(context),\n          onShowOptions: () => showOptionSheet(context),\n        ),\n      ),\n    );\n  }\n}","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/mobile/lib/presentation/pages/person.page.dart#L64-L100","documentation":"This exception is thrown by the person page's timelineServiceProvider override when currentUserProvider is null. The person timeline requires both the logged-in user id and the person id (timelineFactoryProvider.person(user.id, _person.id)), so a null user makes the service unconstructible and the provider throws. It means the person timeline page was built without an authenticated session.","triggerScenarios":"Reading timelineServiceProvider on the person page while currentUserProvider is null — auth stream not yet emitted, session cleared by logout/expiry, or navigation to a person before login finishes.","commonSituations":"Tapping a person notification/deep link before the app finishes sign-in; session expiry while browsing a person timeline; app state restoration pushing this page ahead of the login gate.","solutions":["Gate person routes on a non-null currentUser via a router redirect to the login page.","Complete auth restoration before restoring the navigation stack that contains the person page.","Ensure currentUserProvider is backed by the live auth stream and updated on login/logout.","Handle auth errors in the stream so a transient failure does not leave currentUser null while the app is signed in."],"exampleFix":"// before\nNavigator.of(context).push(PersonPage.route(person));\n// after\nfinal user = ref.read(currentUserProvider);\nif (user == null) {\n  context.go('/login');\n} else {\n  Navigator.of(context).push(PersonPage.route(person));\n}","handlingStrategy":"validation","validationCode":"final user = ref.read(currentUserProvider);\nif (user == null) {\n  context.go('/login');\n  return;\n}","typeGuard":"bool isSignedIn(User? user) => user != null && user.id.isNotEmpty;","tryCatchPattern":"try {\n  final service = ref.read(timelineServiceProvider);\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 person links/notifications through an auth guard before pushing the page.","Complete sign-in before restoring a saved navigation stack containing person pages.","Keep currentUserProvider driven by the live auth state stream.","Navigate away on logout so person pages never rebuild with a null user.","Cover person deep links with a signed-out integration test."],"tags":["flutter","riverpod","authentication","null-user"],"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"}