immich-app/immich · error · BadRequestException
Not found or no ${request.permission} access
Error message
Not found or no ${request.permission} access What it means
A BadRequestException (HTTP 400) thrown by requireAccess when the set of resource ids the user is allowed to act on does not exactly equal the set of ids requested. checkAccess returns the allowed id subset; if any requested id is missing (either it does not exist or the user lacks the permission), the whole request is rejected.
Source
Thrown at server/src/utils/access.ts:40
auth: AuthDto;
permission: Permission;
ids: Set<string> | string[];
};
type SharedLinkAccessRequest = { sharedLink: AuthSharedLink; permission: Permission; ids: Set<string> };
type OtherAccessRequest = { auth: AuthDto; permission: Permission; ids: Set<string> };
export const requireUploadAccess = (auth: AuthDto | null): AuthDto => {
if (!auth || (auth.sharedLink && !auth.sharedLink.allowUpload)) {
throw new UnauthorizedException();
}
return auth;
};
export const requireAccess = async (access: AccessRepository, request: AccessRequest) => {
const allowedIds = await checkAccess(access, request);
if (!areSetsEqual(new Set(request.ids), allowedIds)) {
throw new BadRequestException(`Not found or no ${request.permission} access`);
}
};
export const checkAccess = async (
access: AccessRepository,
{ ids, auth, permission }: AccessRequest,
): Promise<Set<string>> => {
const idSet = Array.isArray(ids) ? new Set(ids) : ids;
if (idSet.size === 0) {
return new Set<string>();
}
return auth.sharedLink
? checkSharedLinkAccess(access, { sharedLink: auth.sharedLink, permission, ids: idSet })
: checkOtherAccess(access, { auth, permission, ids: idSet });
};
const checkSharedLinkAccess = async (View on GitHub (pinned to 199723261c)
Solutions
- Verify each id belongs to the calling user (or is shared with them) before the request.
- Refresh client-side id lists after deletions to avoid referencing removed resources.
- For batch operations, split ids and operate only on those the user owns, or request access to the others.
- Check the exact permission in the error message to know which access path failed.
Example fix
// before await api.deleteAssets(['a','b','c']); // b owned by someone else -> 400 // after const owned = await api.getMyAssets(); const deletable = ['a','b','c'].filter((id) => owned.some((a) => a.id === id)); await api.deleteAssets(deletable);
Defensive patterns
Strategy: validation
Validate before calling
async function filterAccessibleIds(auth, permission, ids) {
// use a bulk access check / ownership filter before the mutating call
const owned = await api.checkAccess(permission, ids);
return ids.filter((id) => owned.has(id));
}
const safeIds = await filterAccessibleIds(auth, permission, ids);
if (safeIds.length !== ids.length) {
return badRequest(`Missing access for ${ids.length - safeIds.length} id(s)`);
} Type guard
const isAccessDeniedError = (e: unknown): boolean =>
typeof e === 'object' && e !== null && (e as any).status === 400 &&
typeof (e as any).message === 'string' && (e as any).message.startsWith('Not found or no'); Try / catch
try {
await api.deleteAssets(ids);
} catch (e) {
if (isAccessDeniedError(e)) {
// retry with only owned ids discovered via search
const owned = await getOwnedIds();
await api.deleteAssets(ids.filter((id) => owned.includes(id)));
return;
}
throw e;
} Prevention
- Operate only on ids returned by the user's own list/search endpoints.
- Refresh id lists after deletions to avoid stale references.
- For batch ops, split by ownership rather than sending mixed id sets.
When it happens
Trigger: Calling any access-gated operation (album update, asset delete, person merge, workflow delete, etc.) with one or more ids the caller does not own or that do not exist. The message includes the permission name, e.g., 'Not found or no AssetUpdate access'.
Common situations: Operating on a resource id from another user; stale client id after a resource was deleted; mixed batch where some ids are valid and others are not; permission not granted (e.g., non-owner trying an owner-only action).
Related errors
- Cannot grant permissions you do not have
- Not authenticated with an API Key
- Forbidden
- Missing required permission: ${requestedPermission}
- Shared link access is only allowed in combination with an al
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/8ed44426914f4d84.
Report an issue: GitHub.