{"record":{"id":"8ed44426914f4d84","repo":"immich-app/immich","slug":"not-found-or-no-request-permission-access","errorCode":null,"errorMessage":"Not found or no ${request.permission} access","messagePattern":"Not found or no (.+?) access","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/utils/access.ts","lineNumber":40,"sourceCode":"  auth: AuthDto;\n  permission: Permission;\n  ids: Set<string> | string[];\n};\n\ntype SharedLinkAccessRequest = { sharedLink: AuthSharedLink; permission: Permission; ids: Set<string> };\ntype OtherAccessRequest = { auth: AuthDto; permission: Permission; ids: Set<string> };\n\nexport const requireUploadAccess = (auth: AuthDto | null): AuthDto => {\n  if (!auth || (auth.sharedLink && !auth.sharedLink.allowUpload)) {\n    throw new UnauthorizedException();\n  }\n  return auth;\n};\n\nexport const requireAccess = async (access: AccessRepository, request: AccessRequest) => {\n  const allowedIds = await checkAccess(access, request);\n  if (!areSetsEqual(new Set(request.ids), allowedIds)) {\n    throw new BadRequestException(`Not found or no ${request.permission} access`);\n  }\n};\n\nexport const checkAccess = async (\n  access: AccessRepository,\n  { ids, auth, permission }: AccessRequest,\n): Promise<Set<string>> => {\n  const idSet = Array.isArray(ids) ? new Set(ids) : ids;\n  if (idSet.size === 0) {\n    return new Set<string>();\n  }\n\n  return auth.sharedLink\n    ? checkSharedLinkAccess(access, { sharedLink: auth.sharedLink, permission, ids: idSet })\n    : checkOtherAccess(access, { auth, permission, ids: idSet });\n};\n\nconst checkSharedLinkAccess = async (","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/utils/access.ts#L22-L58","documentation":"requireAccess evaluates the requested permission over the given id set and compares the allowed set to the requested set. If they are not exactly equal — any requested id is missing, forbidden, or the resource does not exist — it throws a 400 Bad Request 'Not found or no <permission> access', deliberately indistinguishable between the two causes.","triggerScenarios":"Any endpoint that calls requireAccess (album/asset/partner/stack operations) with ids where at least one id either does not exist, belongs to another user, or the session lacks the requested permission (e.g. album.share, asset.delete) for it.","commonSituations":"Stale client references to deleted assets/albums; sharing links or collaboration where the partner hasn't granted the permission; IDs from another server/account; bulk operations where one id in the list fails and the whole request is rejected; race where a resource is deleted between listing and acting.","solutions":["Verify every id in the request exists and belongs to you (or is shared with you) — fetch current lists before the bulk call.","Check the required permission in the message and ensure the resource was shared with that permission level (e.g. edit vs view).","Remove stale ids from client state and retry; refresh the album/asset list.","For bulk requests, split into smaller batches and/or check access per id (checkAccess) to isolate the offending id."],"exampleFix":"// before\nawait api.deleteAssets({ ids: allIds }); // one stale id fails everything\n// after\nconst owned = await checkAccess(access, { auth, permission: 'asset.delete', ids: allIds });\nawait api.deleteAssets({ ids: [...owned] });","handlingStrategy":"validation","validationCode":"const allowed = await checkAccess(access, { auth, permission: 'asset.delete', ids });\nif (allowed.size !== ids.length) {\n  // filter to permitted ids or abort before the real call\n  ids = ids.filter((id) => allowed.has(id));\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.deleteAlbums({ ids });\n} catch (e) {\n  if (e instanceof BadRequestException && e.message.startsWith('Not found or no')) {\n    // refresh state; retry per-id to isolate the offending id\n  }\n}","preventionTips":["Refresh resource lists before bulk operations to drop deleted ids.","Verify share/permission level for shared resources before acting on them.","Process bulk requests in chunks and check access per id.","Never act on ids obtained from another user's responses."],"tags":["access-control","authorization","not-found","permissions"],"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"}