{"record":{"id":"cc80d8f5919a49c2","repo":"immich-app/immich","slug":"you-may-not-access-another-user-s-locked-timeline","errorCode":null,"errorMessage":"You may not access another user's locked timeline","messagePattern":"You may not access another user's locked timeline","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/timeline.service.ts","lineNumber":64,"sourceCode":"\n  private async timeBucketChecks(auth: AuthDto, dto: TimeBucketDto) {\n    if (dto.visibility === AssetVisibility.Locked) {\n      requireElevatedPermission(auth);\n    }\n\n    if (dto.albumId) {\n      await this.requireAccess({ auth, permission: Permission.AlbumRead, ids: [dto.albumId] });\n    } else {\n      dto.userId ||= auth.user.id;\n    }\n\n    if (dto.userId) {\n      await this.requireAccess({ auth, permission: Permission.TimelineRead, ids: [dto.userId] });\n      if (dto.visibility === AssetVisibility.Archive) {\n        await this.requireAccess({ auth, permission: Permission.ArchiveRead, ids: [dto.userId] });\n      }\n      if (dto.visibility === AssetVisibility.Locked && dto.userId !== auth.user.id) {\n        throw new BadRequestException(\"You may not access another user's locked timeline\");\n      }\n    }\n\n    if (dto.tagId) {\n      await this.requireAccess({ auth, permission: Permission.TagRead, ids: [dto.tagId] });\n    }\n\n    if (auth.sharedLink && !auth.sharedLink.showExif) {\n      dto.withCoordinates = false;\n    }\n\n    if (dto.withPartners) {\n      const isRequestedLocked = dto.visibility === AssetVisibility.Locked;\n      const isRequestedArchived = dto.visibility === AssetVisibility.Archive || dto.visibility === undefined;\n      const isRequestedFavorite = dto.isFavorite === true || dto.isFavorite === false;\n      const isRequestedTrash = dto.isTrashed === true;\n\n      if (isRequestedLocked || isRequestedArchived || isRequestedFavorite || isRequestedTrash) {","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/timeline.service.ts#L46-L82","documentation":"Thrown by TimelineService.timeBucketChecks when a caller requests a locked-visibility asset timeline for a user other than themselves. Locked assets are a per-user restricted visibility; even with TimelineRead access, only the owner may view their locked timeline. It is a 400 BadRequest enforcing ownership of locked content.","triggerScenarios":"GET /api/timeline/buckets (or a single bucket) with userId set to another user's ID and visibility=locked. The requireAccess(TimelineRead) check may pass (e.g. admin/partner access), but the explicit ownership comparison dto.userId !== auth.user.id still rejects the request.","commonSituations":"Shared album or partner flows that pass a partner's userId while visibility defaults to or is explicitly set to locked; admin dashboards iterating all users including locked visibility; copy-pasted queries with hardcoded visibility filters.","solutions":["Query only your own locked timeline (omit userId or use your own user id)","Change the requested visibility to archive/undefined instead of locked when viewing another user","Remove the visibility=locked filter when fetching partner/shared timelines"],"exampleFix":"// before\nawait api.getTimeBuckets({ userId: partnerId, visibility: AssetVisibility.Locked });\n// after\nawait api.getTimeBuckets({ userId: partnerId, visibility: AssetVisibility.Archive });","handlingStrategy":"validation","validationCode":"if (visibility === 'locked' && userId && userId !== auth.user.id) {\n  throw new Error('cannot view another user\\'s locked timeline');\n}","typeGuard":null,"tryCatchPattern":"try { await api.getTimeBuckets(params); } catch (e) {\n  if (e.response?.status === 400 && /locked timeline/.test(e.response?.data?.message ?? '')) {\n    return api.getTimeBuckets({ ...params, visibility: undefined });\n  }\n  throw e;\n}","preventionTips":["Never request locked visibility for a userId other than your own","Guard UI toggles that combine partner/user switching with locked filters","Default to omitting visibility when querying other users"],"tags":["permission","ownership"],"backgroundTag":"permission-denied","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"}